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

This is the master POM file for Oracle's Implementation of the JSF 2.2 Specification.

The newest version!
/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 1997-2012 Oracle and/or its affiliates. All rights reserved.
 *
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, the "License").  You
 * may not use this file except in compliance with the License.  You can
 * obtain a copy of the License at
 * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
 * or packager/legal/LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 *
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at packager/legal/LICENSE.txt.
 *
 * GPL Classpath Exception:
 * Oracle designates this particular file as subject to the "Classpath"
 * exception as provided by Oracle in the GPL Version 2 section of the License
 * file that accompanied this code.
 *
 * Modifications:
 * If applicable, add the following below the License Header, with the fields
 * enclosed by brackets [] replaced by your own identifying information:
 * "Portions Copyright [year] [name of copyright owner]"
 *
 * Contributor(s):
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 */

package com.sun.faces.renderkit;

import java.io.IOException;
import java.io.Writer;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.math.BigDecimal;

import javax.faces.FacesException;
import javax.faces.FactoryFinder;
import javax.faces.application.Resource;
import javax.faces.application.ResourceHandler;
import javax.faces.application.ProjectStage;
import javax.faces.application.Application;
import javax.faces.application.FacesMessage;
import javax.faces.component.behavior.*;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import javax.faces.context.ExternalContext;
import javax.faces.model.SelectItem;
import javax.faces.render.RenderKit;
import javax.faces.render.RenderKitFactory;
import javax.faces.render.ResponseStateManager;
import javax.faces.render.Renderer;

import com.sun.faces.RIConstants;
import com.sun.faces.config.WebConfiguration;
import com.sun.faces.config.WebConfiguration.BooleanWebContextInitParameter;
import com.sun.faces.facelets.util.DevTools;
import com.sun.faces.util.FacesLogger;
import com.sun.faces.util.Util;
import com.sun.faces.util.RequestStateManager;

import javax.faces.component.*;
import javax.faces.component.html.HtmlMessages;

/**
 * 

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" }; /** *

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 JSF 2.0 will remove the need for this. */ private static final String OPTIMIZED_PACKAGE = "javax.faces.component."; /** * IMPLEMENTATION NOTE: This must be kept in sync with the Key * in UIComponentBase$AttributesMap and HtmlComponentGenerator. * * Hopefully JSF 2.0 will remove the need for this. */ private static final String ATTRIBUTES_THAT_ARE_SET_KEY = UIComponentBase.class.getName() + ".attributesThatAreSet"; protected static final Logger LOGGER = FacesLogger.RENDERKIT.getLogger(); // ------------------------------------------------------------ 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 javax.faces.model.SelectItem} * instances representing the available options for this component, * assembled from the set of {@link javax.faces.component.UISelectItem} * and/or {@link javax.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 javax.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 javax.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 javax.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 */ 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)) { //noinspection unchecked 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(); if (null != behaviors && behaviors.containsKey("click")) { behaviorEventName = "click"; } } renderHandler(context, component, params, handlerName, userHandler, behaviorEventName, submitTarget, needsSubmit, false); } 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 * javax.faces.component or javax.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= 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 { 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("-", "$_"); } /** *

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 renderJsfJs(FacesContext context) throws IOException { if (hasScriptBeenRendered(context)) { // Already included, return return; } final String name = "jsf.js"; final String library = "javax.faces"; if (hasResourceBeenInstalled(context, name, library)) { setScriptAsRendered(context); return; } // Since we've now determined that it's not in the page, we need to add it. UIOutput output = new UIOutput(); output.setRendererType("javax.faces.resource.Script"); output.getAttributes().put("name", name); output.getAttributes().put("library", library); output.encodeAll(context); // Set the context to record script as included setScriptAsRendered(context); } public static boolean hasResourceBeenInstalled(FacesContext ctx, String name, String library) { UIViewRoot viewRoot = ctx.getViewRoot(); ListIterator iter = (viewRoot.getComponentResources(ctx, "head")).listIterator(); while (iter.hasNext()) { UIComponent resource = (UIComponent)iter.next(); String rname = (String)resource.getAttributes().get("name"); String rlibrary = (String)resource.getAttributes().get("library"); if (name.equals(rname) && library.equals(rlibrary)) { // Set the context to record script as included return true; } } iter = (viewRoot.getComponentResources(ctx, "body")).listIterator(); while (iter.hasNext()) { UIComponent resource = (UIComponent)iter.next(); String rname = (String)resource.getAttributes().get("name"); String rlibrary = (String)resource.getAttributes().get("library"); if (name.equals(rname) && library.equals(rlibrary)) { // Set the context to record script as included return true; } } iter = (viewRoot.getComponentResources(ctx, "form")).listIterator(); while (iter.hasNext()) { UIComponent resource = (UIComponent)iter.next(); String rname = (String)resource.getAttributes().get("name"); String rlibrary = (String)resource.getAttributes().get("library"); if (name.equals(rname) && library.equals(rlibrary)) { // Set the context to record script as included 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("javax_faces_developmentstage_messages"); Renderer messagesRenderer = ctx.getRenderKit().getRenderer(HtmlMessages.COMPONENT_FAMILY, "javax.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, "jsf.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, "jsf.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 jsf.ajax.request() or via a submitting // behavior. public static boolean isPartialOrBehaviorAction(FacesContext context, String clientId) { if ((clientId == null) || (clientId.length() == 0)) { return false; } ExternalContext external = context.getExternalContext(); Map params = external.getRequestParameterMap(); String source = params.get("javax.faces.source"); if (!clientId.equals(source)) { return false; } // First check for a Behavior action event. String behaviorEvent = params.get("javax.faces.behavior.event"); if (null != behaviorEvent) { return ("action".equals(behaviorEvent)); } // Not a Behavior-related request. Check for jsf.ajax.request() // request params. String partialEvent = params.get("javax.faces.partial.event"); 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; } /** * @param context the FacesContext for the current request * * @return true If the add/remove javascript * has been rendered, otherwise false */ public static boolean hasScriptBeenRendered(FacesContext context) { return RequestStateManager.containsKey(context, RequestStateManager.SCRIPT_STATE); } /** *

Set a flag to indicate that the add/remove javascript * has been rendered for the current form. * * @param context the FacesContext of the current request */ public static void setScriptAsRendered(FacesContext context) { RequestStateManager.set(context, RequestStateManager.SCRIPT_STATE, Boolean.TRUE); } /** *

* 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"); WebConfiguration webConfig = WebConfiguration.getInstance(); if (libName == null && resName.startsWith(webConfig.getOptionValue(WebConfiguration.WebContextInitParameter.WebAppContractsDirectory))) { 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)); } } } // --------------------------------------------------------- Private Methods // Appends a script to a jsf.util.chain() call private static void appendScriptToChain(StringBuilder builder, String script) { if ((script == null) || (script.length() == 0)) { return; } if (builder.length() == 0) { builder.append("jsf.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. private 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 jsf.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.jsfcljs() 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.jsfcljs(document.getElementById('"); builder.append(formClientId); builder.append("'),{"); appendProperty(builder, componentClientId, componentClientId); if ((null != params) && (!params.isEmpty())) { String namingContainerId = ""; WebConfiguration webConfig = WebConfiguration.getInstance(); boolean namespaceParameters = webConfig.isOptionEnabled(BooleanWebContextInitParameter.NamespaceParameters); if (namespaceParameters) { UIViewRoot viewRoot = context.getViewRoot(); if (viewRoot instanceof NamingContainer) { namingContainerId = viewRoot.getContainerClientId(context); } } for (ClientBehaviorContext.Parameter param : params) { appendProperty(builder, namingContainerId + 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 || component instanceof ActionSource2)); 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.jsfcljs() "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.jsfcljs() * "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 static 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.jsfcljs() script SUBMIT_ONLY, // Indicates that we've got a chain CHAIN } // ---------------------------------------------------------- Nested Classes } // END RenderKitUtils




© 2015 - 2024 Weber Informatics LLC | Privacy Policy