com.sun.faces.util.Util.java Source code

Java tutorial

Introduction

Here is the source code for com.sun.faces.util.Util.java

Source

/*
 * $Id: Util.java,v 1.143.6.1.2.5.2.1 2006/04/12 19:32:33 ofung Exp $
 */

/*
 * The contents of this file are subject to the terms
 * of the Common Development and Distribution License
 * (the License). You may not use this file except in
 * compliance with the License.
 * 
 * You can obtain a copy of the License at
 * https://javaserverfaces.dev.java.net/CDDL.html or
 * legal/CDDLv1.0.txt. 
 * See the License for the specific language governing
 * permission and limitations under the License.
 * 
 * When distributing Covered Code, include this CDDL
 * Header Notice in each file and include the License file
 * at legal/CDDLv1.0.txt.    
 * If applicable, add the following below the CDDL Header,
 * with the fields enclosed by brackets [] replaced by
 * your own identifying information:
 * "Portions Copyrighted [year] [name of copyright owner]"
 * 
 * [Name of File] [ver.__] [Date]
 * 
 * Copyright 2006 Sun Microsystems Inc. All Rights Reserved
 */

// Util.java

package com.sun.faces.util;

import com.sun.faces.RIConstants;
import com.sun.faces.el.impl.ExpressionEvaluator;
import com.sun.faces.el.impl.ExpressionEvaluatorImpl;
import com.sun.faces.renderkit.RenderKitImpl;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import javax.faces.FacesException;
import javax.faces.FactoryFinder;
import javax.faces.application.Application;
import javax.faces.application.ApplicationFactory;
import javax.faces.application.FacesMessage;
import javax.faces.application.StateManager;
import javax.faces.application.ViewHandler;
import javax.faces.component.UIComponent;
import javax.faces.component.UISelectItem;
import javax.faces.component.UISelectItems;
import javax.faces.context.FacesContext;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContextFactory;
import javax.faces.context.ResponseWriter;
import javax.faces.convert.Converter;
import javax.faces.el.MethodBinding;
import javax.faces.el.ReferenceSyntaxException;
import javax.faces.el.ValueBinding;
import javax.faces.lifecycle.LifecycleFactory;
import javax.faces.model.SelectItem;
import javax.faces.render.RenderKit;
import javax.faces.render.RenderKitFactory;
import javax.faces.render.ResponseStateManager;
import javax.servlet.ServletContext;

import java.io.IOException;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.Locale;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.HashMap;

/**
 * <B>Util</B> is a class ...
 * <p/>
 * <B>Lifetime And Scope</B> <P>
 *
 * @version $Id: Util.java,v 1.143.6.1.2.5.2.1 2006/04/12 19:32:33 ofung Exp $
 */

public class Util extends Object {

    //
    // Private/Protected Constants
    //
    private static Map renderKitMap = new HashMap(4);

    // Log instance for this class
    protected static Log log = LogFactory.getLog(Util.class);

    /**
     * The parser implementation for handling Faces RE expressions.
     */
    private static final ExpressionEvaluator FACES_EXPRESSION_EVALUATOR = new ExpressionEvaluatorImpl(
            RIConstants.FACES_RE_PARSER);

    private static final String RENDER_KIT_IMPL_REQ = RIConstants.FACES_PREFIX + "renderKitImplForRequest";

    // README - make sure to add the message identifier constant
    // (ex: Util.CONVERSION_ERROR_MESSAGE_ID) and the number of substitution
    // parameters to test/com/sun/faces/util/TestUtil_messages (see comment there).

    /**
     * The message identifier of the {@link Message} to be created as
     * a result of type conversion error.
     */
    public static final String CONVERSION_ERROR_MESSAGE_ID = "com.sun.faces.TYPECONVERSION_ERROR";

    /**
     * The message identifier of the {@link Message} to be created if
     * there is model update failure.
     */
    public static final String MODEL_UPDATE_ERROR_MESSAGE_ID = "com.sun.faces.MODELUPDATE_ERROR";

    public static final String FACES_CONTEXT_CONSTRUCTION_ERROR_MESSAGE_ID = "com.sun.faces.FACES_CONTEXT_CONSTRUCTION_ERROR";

    public static final String NULL_COMPONENT_ERROR_MESSAGE_ID = "com.sun.faces.NULL_COMPONENT_ERROR";

    public static final String NULL_REQUEST_VIEW_ERROR_MESSAGE_ID = "com.sun.faces.NULL_REQUEST_VIEW_ERROR";

    public static final String NULL_RESPONSE_VIEW_ERROR_MESSAGE_ID = "com.sun.faces.NULL_RESPONSE_VIEW_ERROR";

    public static final String REQUEST_VIEW_ALREADY_SET_ERROR_MESSAGE_ID = "com.sun.faces.REQUEST_VIEW_ALREADY_SET_ERROR";

    public static final String NULL_MESSAGE_ERROR_MESSAGE_ID = "com.sun.faces.NULL_MESSAGE_ERROR";

    public static final String NULL_PARAMETERS_ERROR_MESSAGE_ID = "com.sun.faces.NULL_PARAMETERS_ERROR";

    public static final String NAMED_OBJECT_NOT_FOUND_ERROR_MESSAGE_ID = "com.sun.faces.NAMED_OBJECT_NOT_FOUND_ERROR";

    public static final String NULL_RESPONSE_STREAM_ERROR_MESSAGE_ID = "com.sun.faces.NULL_RESPONSE_STREAM_ERROR";

    public static final String NULL_RESPONSE_WRITER_ERROR_MESSAGE_ID = "com.sun.faces.NULL_RESPONSE_WRITER_ERROR";

    public static final String NULL_EVENT_ERROR_MESSAGE_ID = "com.sun.faces.NULL_EVENT_ERROR";

    public static final String NULL_HANDLER_ERROR_MESSAGE_ID = "com.sun.faces.NULL_HANDLER_ERROR";

    public static final String NULL_CONTEXT_ERROR_MESSAGE_ID = "com.sun.faces.NULL_CONTEXT_ERROR";

    public static final String NULL_LOCALE_ERROR_MESSAGE_ID = "com.sun.faces.NULL_LOCALE_ERROR";

    public static final String SUPPORTS_COMPONENT_ERROR_MESSAGE_ID = "com.sun.faces.SUPPORTS_COMPONENT_ERROR";

    public static final String MISSING_RESOURCE_ERROR_MESSAGE_ID = "com.sun.faces.MISSING_RESOURCE_ERROR";

    public static final String MISSING_CLASS_ERROR_MESSAGE_ID = "com.sun.faces.MISSING_CLASS_ERROR";
    public static final String COMPONENT_NOT_FOUND_ERROR_MESSAGE_ID = "com.sun.faces.COMPONENT_NOT_FOUND_ERROR";
    public static final String LIFECYCLE_ID_ALREADY_ADDED_ID = "com.sun.faces.LIFECYCLE_ID_ALREADY_ADDED";

    public static final String LIFECYCLE_ID_NOT_FOUND_ERROR_MESSAGE_ID = "com.sun.faces.LIFECYCLE_ID_NOT_FOUND";

    public static final String PHASE_ID_OUT_OF_BOUNDS_ERROR_MESSAGE_ID = "com.sun.faces.PHASE_ID_OUT_OF_BOUNDS";

    public static final String CANT_CREATE_LIFECYCLE_ERROR_MESSAGE_ID = "com.sun.faces.CANT_CREATE_LIFECYCLE_ERROR";

    public static final String ILLEGAL_MODEL_REFERENCE_ID = "com.sun.faces.ILLEGAL_MODEL_REFERENCE";

    public static final String ATTRIBUTE_NOT_SUPORTED_ERROR_MESSAGE_ID = "com.sun.faces.ATTRIBUTE_NOT_SUPORTED";

    public static final String FILE_NOT_FOUND_ERROR_MESSAGE_ID = "com.sun.faces.FILE_NOT_FOUND";

    public static final String CANT_PARSE_FILE_ERROR_MESSAGE_ID = "com.sun.faces.CANT_PARSE_FILE";

    public static final String CANT_INSTANTIATE_CLASS_ERROR_MESSAGE_ID = "com.sun.faces.CANT_INSTANTIATE_CLASS";

    public static final String ILLEGAL_CHARACTERS_ERROR_MESSAGE_ID = "com.sun.faces.ILLEGAL_CHARACTERS_ERROR";

    public static final String NOT_NESTED_IN_FACES_TAG_ERROR_MESSAGE_ID = "com.sun.faces.NOT_NESTED_IN_FACES_TAG_ERROR";

    public static final String NULL_BODY_CONTENT_ERROR_MESSAGE_ID = "com.sun.faces.NULL_BODY_CONTENT_ERROR";

    public static final String SAVING_STATE_ERROR_MESSAGE_ID = "com.sun.faces.SAVING_STATE_ERROR";

    public static final String RENDERER_NOT_FOUND_ERROR_MESSAGE_ID = "com.sun.faces.RENDERER_NOT_FOUND";

    public static final String MAXIMUM_EVENTS_REACHED_ERROR_MESSAGE_ID = "com.sun.faces.MAXIMUM_EVENTS_REACHED";

    public static final String NULL_CONFIGURATION_ERROR_MESSAGE_ID = "com.sun.faces.NULL_CONFIGURATION";

    public static final String ERROR_OPENING_FILE_ERROR_MESSAGE_ID = "com.sun.faces.ERROR_OPENING_FILE";

    public static final String ERROR_REGISTERING_DTD_ERROR_MESSAGE_ID = "com.sun.faces.ERROR_REGISTERING_DTD";

    public static final String INVALID_INIT_PARAM_ERROR_MESSAGE_ID = "com.sun.faces.INVALID_INIT_PARAM";

    public static final String ERROR_SETTING_BEAN_PROPERTY_ERROR_MESSAGE_ID = "com.sun.faces.ERROR_SETTING_BEAN_PROPERTY";

    public static final String ERROR_GETTING_VALUE_BINDING_ERROR_MESSAGE_ID = "com.sun.faces.ERROR_GETTING_VALUE_BINDING";

    public static final String ERROR_GETTING_VALUEREF_VALUE_ERROR_MESSAGE_ID = "com.sun.faces.ERROR_GETTING_VALUEREF_VALUE";

    public static final String CANT_INTROSPECT_CLASS_ERROR_MESSAGE_ID = "com.sun.faces.CANT_INTROSPECT_CLASS";

    public static final String CANT_CONVERT_VALUE_ERROR_MESSAGE_ID = "com.sun.faces.CANT_CONVERT_VALUE";

    public static final String INVALID_SCOPE_LIFESPAN_ERROR_MESSAGE_ID = "com.sun.faces.INVALID_SCOPE_LIFESPAN";

    public static final String CONVERTER_NOT_FOUND_ERROR_MESSAGE_ID = "com.sun.faces.CONVERTER_NOT_FOUND_ERROR";

    public static final String VALIDATOR_NOT_FOUND_ERROR_MESSAGE_ID = "com.sun.faces.VALIDATOR_NOT_FOUND_ERROR";

    public static final String CANT_LOAD_CLASS_ERROR_MESSAGE_ID = "com.sun.faces.CANT_INSTANTIATE_CLASS";

    public static final String ENCODING_ERROR_MESSAGE_ID = "com.sun.faces.ENCODING_ERROR";

    public static final String ILLEGAL_IDENTIFIER_LVALUE_MODE_ID = "com.sun.faces.ILLEGAL_IDENTIFIER_LVALUE_MODE";

    public static final String VALIDATION_ID_ERROR_ID = "com.sun.faces.VALIDATION_ID_ERROR";

    public static final String VALIDATION_EL_ERROR_ID = "com.sun.faces.VALIDATION_EL_ERROR";

    public static final String VALIDATION_COMMAND_ERROR_ID = "com.sun.faces.VALIDATION_COMMAND_ERROR";

    public static final String CONTENT_TYPE_ERROR_MESSAGE_ID = "com.sun.faces.CONTENT_TYPE_ERROR";

    public static final String COMPONENT_NOT_FOUND_IN_VIEW_WARNING_ID = "com.sun.faces.COMPONENT_NOT_FOUND_IN_VIEW_WARNING";

    public static final String ILLEGAL_ATTEMPT_SETTING_VIEWHANDLER_ID = "com.sun.faces.ILLEGAL_ATTEMPT_SETTING_VIEWHANDLER";

    public static final String ILLEGAL_ATTEMPT_SETTING_STATEMANAGER_ID = "com.sun.faces.ILLEGAL_ATTEMPT_SETTING_STATEMANAGER";

    public static final String INVALID_MESSAGE_SEVERITY_IN_CONFIG_ID = "com.sun.faces.INVALID_MESSAGE_SEVERITY_IN_CONFIG";

    public static final String CANT_CLOSE_INPUT_STREAM_ID = "com.sun.faces.CANT_CLOSE_INPUT_STREAM";

    public static final String DUPLICATE_COMPONENT_ID_ERROR_ID = "com.sun.faces.DUPLICATE_COMPONENT_ID_ERROR";

    public static final String FACES_SERVLET_MAPPING_CANNOT_BE_DETERMINED_ID = "com.sun.faces.FACES_SERVLET_MAPPING_CANNOT_BE_DETERMINED";

    public static final String ILLEGAL_VIEW_ID_ID = "com.sun.faces.ILLEGAL_VIEW_ID";

    public static final String INVALID_EXPRESSION_ID = "com.sun.faces.INVALID_EXPRESSION";
    public static final String NULL_FORVALUE_ID = "com.sun.faces.NULL_FORVALUE";
    public static final String EMPTY_PARAMETER_ID = "com.sun.faces.EMPTY_PARAMETER";
    public static final String ASSERTION_FAILED_ID = "com.sun.faces.ASSERTION_FAILED";
    public static final String OBJECT_CREATION_ERROR_ID = "com.sun.faces.OBJECT_CREATION_ERROR";

    public static final String CYCLIC_REFERENCE_ERROR_ID = "com.sun.faces.CYCLIC_REFERENCE_ERROR";

    public static final String NO_DTD_FOUND_ERROR_ID = "com.sun.faces.NO_DTD_FOUND_ERROR";

    public static final String MANAGED_BEAN_CANNOT_SET_LIST_ARRAY_PROPERTY_ID = "com.sun.faces.MANAGED_BEAN_CANNOT_SET_LIST_ARRAY_PROPERTY";

    public static final String MANAGED_BEAN_EXISTING_VALUE_NOT_LIST_ID = "com.sun.faces.MANAGED_BEAN_EXISTING_VALUE_NOT_LIST";

    public static final String MANAGED_BEAN_CANNOT_SET_MAP_PROPERTY_ID = "com.sun.faces.MANAGED_BEAN_CANNOT_SET_MAP_PROPERTY";

    public static final String MANAGED_BEAN_TYPE_CONVERSION_ERROR_ID = "com.sun.faces.MANAGED_BEAN_TYPE_CONVERSION_ERROR";

    public static final String COMMAND_LINK_NO_FORM_MESSAGE_ID = "com.sun.faces.COMMAND_LINK_NO_FORM_MESSAGE";

    // README - make sure to add the message identifier constant
    // (ex: Util.CONVERSION_ERROR_MESSAGE_ID) and the number of substitution
    // parameters to test/com/sun/faces/util/TestUtil_messages (see comment there).

    //
    // Class Variables
    //

    /**
     * This array contains attributes that have a boolean value in JSP,
     * but have have no value in HTML.  For example "disabled" or
     * "readonly". <P>
     *
     * @see renderBooleanPassthruAttributes
     */

    private static String booleanPassthruAttributes[] = { "disabled", "readonly", "ismap" };

    /**
     * This array contains attributes whose value is just rendered
     * straight to the content.  This array should only contain
     * attributes that require no interpretation by the Renderer.  If an
     * attribute requires interpretation by a Renderer, it should be
     * removed from this array.<P>
     *
     * @see renderPassthruAttributes
     */
    private static String passthruAttributes[] = { "accept", "accesskey", "alt", "bgcolor", "border", "cellpadding",
            "cellspacing", "charset", "cols", "coords", "dir", "enctype", "frame", "height", "hreflang", "lang",
            "longdesc", "maxlength", "onblur", "onchange", "onclick", "ondblclick", "onfocus", "onkeydown",
            "onkeypress", "onkeyup", "onload", "onmousedown", "onmousemove", "onmouseout", "onmouseover",
            "onmouseup", "onreset", "onselect", "onsubmit", "onunload", "rel", "rev", "rows", "rules", "shape",
            "size", "style", "summary", "tabindex", "target", "title", "usemap", "width" };

    //NOTE - "type" was deliberately skipped from the list of passthru
    //attrs above All renderers that need this attribute should manually
    //pass it.

    //
    // Instance Variables
    //

    // Attribute Instance Variables

    // Relationship Instance Variables

    //
    // Constructors and Initializers    
    //

    private Util() {
        throw new IllegalStateException();
    }

    //
    // Class methods
    //
    public static Class loadClass(String name, Object fallbackClass) throws ClassNotFoundException {
        ClassLoader loader = Util.getCurrentLoader(fallbackClass);
        return loader.loadClass(name);
    }

    public static ClassLoader getCurrentLoader(Object fallbackClass) {
        ClassLoader loader = Thread.currentThread().getContextClassLoader();
        if (loader == null) {
            loader = fallbackClass.getClass().getClassLoader();
        }
        return loader;
    }

    /**
     * Called by the RI to get the IMPL_MESSAGES MessageFactory
     * instance and get a message on it.
     */

    public static synchronized String getExceptionMessageString(String messageId, Object params[]) {
        String result = null;

        FacesMessage message = MessageFactory.getMessage(messageId, params);
        if (null != message) {
            result = message.getSummary();
        }

        if (null == result) {
            result = "null MessageFactory";
        }
        return result;
    }

    public static synchronized String getExceptionMessageString(String messageId) {
        return Util.getExceptionMessageString(messageId, null);
    }

    public static synchronized FacesMessage getExceptionMessage(String messageId, Object params[]) {
        return MessageFactory.getMessage(messageId, params);
    }

    public static synchronized FacesMessage getExceptionMessage(String messageId) {
        return Util.getExceptionMessage(messageId, null);
    }

    /**
     * Verify the existence of all the factories needed by faces.  Create
     * and install the default RenderKit into the ServletContext. <P>
     *
     * @see javax.faces.FactoryFinder
     */

    public static void verifyFactoriesAndInitDefaultRenderKit(ServletContext context) throws FacesException {
        RenderKitFactory renderKitFactory = null;
        LifecycleFactory lifecycleFactory = null;
        FacesContextFactory facesContextFactory = null;
        ApplicationFactory applicationFactory = null;
        RenderKit defaultRenderKit = null;

        renderKitFactory = (RenderKitFactory) FactoryFinder.getFactory(FactoryFinder.RENDER_KIT_FACTORY);
        Util.doAssert(null != renderKitFactory);

        lifecycleFactory = (LifecycleFactory) FactoryFinder.getFactory(FactoryFinder.LIFECYCLE_FACTORY);
        Util.doAssert(null != lifecycleFactory);

        facesContextFactory = (FacesContextFactory) FactoryFinder.getFactory(FactoryFinder.FACES_CONTEXT_FACTORY);
        Util.doAssert(null != facesContextFactory);

        applicationFactory = (ApplicationFactory) FactoryFinder.getFactory(FactoryFinder.APPLICATION_FACTORY);
        Util.doAssert(null != applicationFactory);

        defaultRenderKit = renderKitFactory.getRenderKit(null, RenderKitFactory.HTML_BASIC_RENDER_KIT);
        if (defaultRenderKit == null) {
            // create default renderkit if doesn't exist
            //
            defaultRenderKit = new RenderKitImpl();
            renderKitFactory.addRenderKit(RenderKitFactory.HTML_BASIC_RENDER_KIT, defaultRenderKit);
        }

        context.setAttribute(RIConstants.HTML_BASIC_RENDER_KIT, defaultRenderKit);

        context.setAttribute(RIConstants.ONE_TIME_INITIALIZATION_ATTR, RIConstants.ONE_TIME_INITIALIZATION_ATTR);
    }

    /**
     * <p>Verifies that the required classes are available on either the
     * ContextClassLoader, or the local ClassLoader.  Currently only
     * checks for the class
     * "javax.servlet.jsp.jstl.fmt.LocalizationContext", which is used
     * for Localization.</p>
     * <p/>
     * <p>The result of the check is saved in the ServletContext
     * attribute RIConstants.HAS_REQUIRED_CLASSES_ATTR.</p>
     * <p/>
     * <p>Algorithm:</p>
     * <p/>
     * <p>Check the ServletContext for the attribute, if found, and the
     * value is false, that means we've checked before, and we don't have
     * the classes, just throw FacesException.  If the value is true,
     * we've checked before and we have the classes, just return.</p>
     */

    public static void verifyRequiredClasses(FacesContext facesContext) throws FacesException {
        Map applicationMap = facesContext.getExternalContext().getApplicationMap();
        Boolean result = null;
        String className = "javax.servlet.jsp.jstl.fmt.LocalizationContext";
        Object[] params = { className };

        // Have we checked before?
        if (null != (result = (Boolean) applicationMap.get(RIConstants.HAS_REQUIRED_CLASSES_ATTR))) {
            // yes, and the check failed.
            if (Boolean.FALSE == result) {
                throw new FacesException(
                        Util.getExceptionMessageString(Util.MISSING_CLASS_ERROR_MESSAGE_ID, params));
            } else {
                // yes, and the check passed.
                return;
            }
        }

        //
        // We've not checked before, so do the check now!
        //

        try {
            Util.loadClass(className, facesContext);
        } catch (ClassNotFoundException e) {
            applicationMap.put(RIConstants.HAS_REQUIRED_CLASSES_ATTR, Boolean.FALSE);
            throw new FacesException(Util.getExceptionMessageString(Util.MISSING_CLASS_ERROR_MESSAGE_ID, params),
                    e);
        }
        applicationMap.put(RIConstants.HAS_REQUIRED_CLASSES_ATTR, Boolean.TRUE);
    }

    /**
     * <p>Return an Iterator over {@link SelectItemWrapper} instances representing the
     * available options for this component, assembled from the set of
     * {@link UISelectItem} and/or {@link UISelectItems} components that are
     * direct children of this component.  If there are no such children, a
     * zero-length array is returned.</p>
     *
     * @param context The {@link FacesContext} for the current request.
     *                If null, the UISelectItems behavior will not work.
     * @throws NullPointerException if <code>context</code>
     *                              is <code>null</code>
     */
    public static Iterator getSelectItems(FacesContext context, UIComponent component) {

        ArrayList list = new ArrayList();
        Iterator kids = component.getChildren().iterator();
        while (kids.hasNext()) {
            UIComponent kid = (UIComponent) kids.next();
            if (kid instanceof UISelectItem) {
                Object value = ((UISelectItem) kid).getValue();
                if (value == null) {
                    UISelectItem item = (UISelectItem) kid;
                    list.add(new SelectItem(item.getItemValue(), item.getItemLabel(), item.getItemDescription(),
                            item.isItemDisabled()));
                } else if (value instanceof SelectItem) {
                    list.add(value);
                } else {
                    throw new IllegalArgumentException(
                            Util.getExceptionMessageString(Util.CONVERSION_ERROR_MESSAGE_ID));
                }
            } else if (kid instanceof UISelectItems && null != context) {
                Object value = ((UISelectItems) kid).getValue();
                if (value instanceof SelectItem) {
                    list.add(value);
                } else if (value instanceof SelectItem[]) {
                    SelectItem items[] = (SelectItem[]) value;
                    for (int i = 0; i < items.length; i++) {
                        list.add(items[i]);
                    }
                } else if (value instanceof Collection) {
                    Iterator elements = ((Collection) value).iterator();
                    while (elements.hasNext()) {
                        list.add(elements.next());
                    }
                } else if (value instanceof Map) {
                    Iterator keys = ((Map) value).keySet().iterator();
                    while (keys.hasNext()) {
                        Object key = keys.next();
                        if (key == null) {
                            continue;
                        }
                        Object val = ((Map) value).get(key);
                        if (val == null) {
                            continue;
                        }
                        list.add(new SelectItem(val.toString(), key.toString(), null));
                    }
                } else {
                    throw new IllegalArgumentException(
                            Util.getExceptionMessageString(Util.CONVERSION_ERROR_MESSAGE_ID));
                }
            }
        }
        return (list.iterator());

    }

    /**
     * Return a Locale instance using the following algorithm: <P>
     * <p/>
     * <UL>
     * <p/>
     * <LI>
     * <p/>
     * If this component instance has an attribute named "bundle",
     * interpret it as a model reference to a LocalizationContext
     * instance accessible via FacesContext.getModelValue().
     * <p/>
     * </LI>
     * <p/>
     * <LI>
     * <p/>
     * If FacesContext.getModelValue() returns a LocalizationContext
     * instance, return its Locale.
     * <p/>
     * </LI>
     * <p/>
     * <LI>
     * <p/>
     * If FacesContext.getModelValue() doesn't return a
     * LocalizationContext, return the FacesContext's Locale.
     * <p/>
     * </LI>
     * <p/>
     * </UL>
     */

    public static Locale getLocaleFromContextOrComponent(FacesContext context, UIComponent component) {
        Locale result = null;
        String bundleName = null, bundleAttr = "bundle";

        Util.parameterNonNull(context);
        Util.parameterNonNull(component);

        // verify our component has the proper attributes for bundle.
        if (null != (bundleName = (String) component.getAttributes().get(bundleAttr))) {
            // verify there is a Locale for this localizationContext
            javax.servlet.jsp.jstl.fmt.LocalizationContext locCtx = null;
            if (null != (locCtx = (javax.servlet.jsp.jstl.fmt.LocalizationContext) (Util
                    .getValueBinding(bundleName)).getValue(context))) {
                result = locCtx.getLocale();
                Util.doAssert(null != result);
            }
        }
        if (null == result) {
            result = context.getViewRoot().getLocale();
        }

        return result;
    }

    /**
     * @return true if the component has any passthru attributes
     */
    // PENDING() it would be much more performant to have the tag be
    // aware of the passthru attributes and have it set a
    // "hasPassthruAttributes" attribute to true if the component has
    // any.  

    public static boolean hasPassThruAttributes(UIComponent component) {
        if (null == component) {
            return false;
        }

        boolean result = false;
        Map attrs = component.getAttributes();
        if (null == attrs) {
            return false;
        }
        int i = 0;
        Object attrVal;
        String empty = "";
        for (i = 0; i < passthruAttributes.length; i++) {
            if (null != (attrVal = attrs.get(passthruAttributes[i])) && !empty.equals(attrVal)) {
                result = true;
                break;
            }
        }
        if (!result) {
            for (i = 0; i < booleanPassthruAttributes.length; i++) {
                if (null != (attrVal = attrs.get(booleanPassthruAttributes[i])) && !empty.equals(attrVal)) {
                    result = true;
                    break;
                }
            }
        }
        return result;
    }

    public static void renderBooleanPassThruAttributes(ResponseWriter writer, UIComponent component)
            throws IOException {
        renderBooleanPassThruAttributes(writer, component, null);
    }

    /**
     * Render any boolean "passthru" attributes.
     * <P>
     *
     * @see passthruAttributes
     */
    public static void renderBooleanPassThruAttributes(ResponseWriter writer, UIComponent component,
            String[] excludes) throws IOException {
        Util.doAssert(null != writer);
        Util.doAssert(null != component);

        int i = 0, len = booleanPassthruAttributes.length, j, jLen = (null != excludes ? excludes.length : 0);
        Object value = null;
        boolean result;
        boolean skip = false;
        for (i = 0; i < len; i++) {
            skip = false;
            if (null != excludes) {
                for (j = 0; j < jLen; j++) {
                    if (null != excludes[j] && excludes[j].equals(booleanPassthruAttributes[i])) {
                        skip = true;
                        break;
                    }
                }
            }
            if (skip) {
                continue;
            }

            value = component.getAttributes().get(booleanPassthruAttributes[i]);
            if (value != null) {
                if (value instanceof Boolean) {
                    result = ((Boolean) value).booleanValue();
                } else {
                    if (!(value instanceof String)) {
                        value = value.toString();
                    }
                    result = (new Boolean((String) value)).booleanValue();
                }
                //PENDING(rogerk) will revisit "null" param soon..
                if (result) {
                    // NOTE:  render things like readonly="readonly" here
                    writer.writeAttribute(booleanPassthruAttributes[i], booleanPassthruAttributes[i],
                            booleanPassthruAttributes[i]);
                    // NOTE:  otherwise render nothing
                }
            }
        }
    }

    public static void renderPassThruAttributes(ResponseWriter writer, UIComponent component) throws IOException {
        renderPassThruAttributes(writer, component, null);
    }

    /**
     * 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.  <P>
     *
     * @see passthruAttributes
     */
    public static void renderPassThruAttributes(ResponseWriter writer, UIComponent component, String[] excludes)
            throws IOException {
        Util.doAssert(null != writer);
        Util.doAssert(null != component);

        int i = 0, len = passthruAttributes.length, j, jLen = (null != excludes ? excludes.length : 0);
        Object value = null;
        boolean skip = false;
        for (i = 0; i < len; i++) {
            skip = false;
            if (null != excludes) {
                for (j = 0; j < jLen; j++) {
                    if (null != excludes[j] && excludes[j].equals(passthruAttributes[i])) {
                        skip = true;
                        break;
                    }
                }
            }
            if (skip) {
                continue;
            }

            value = component.getAttributes().get(passthruAttributes[i]);
            if (value != null && shouldRenderAttribute(value)) {
                if (!(value instanceof String)) {
                    value = value.toString();
                }
                //PENDING(rogerk) will revisit "null" param soon..
                writer.writeAttribute(passthruAttributes[i], value, passthruAttributes[i]);
            }
        }
    }

    /**
     * @return true if and only if the argument
     *         <code>attributeVal</code> 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 spec.
     */

    private static boolean shouldRenderAttribute(Object attributeVal) {
        if (attributeVal instanceof Boolean
                && ((Boolean) attributeVal).booleanValue() == Boolean.FALSE.booleanValue()) {
            return false;
        } else if (attributeVal instanceof Integer && ((Integer) attributeVal).intValue() == Integer.MIN_VALUE) {
            return false;
        } else if (attributeVal instanceof Double && ((Double) attributeVal).doubleValue() == Double.MIN_VALUE) {
            return false;
        } else if (attributeVal instanceof Character
                && ((Character) attributeVal).charValue() == Character.MIN_VALUE) {
            return false;
        } else if (attributeVal instanceof Float && ((Float) attributeVal).floatValue() == Float.MIN_VALUE) {
            return false;
        } else if (attributeVal instanceof Short && ((Short) attributeVal).shortValue() == Short.MIN_VALUE) {
            return false;
        } else if (attributeVal instanceof Byte && ((Byte) attributeVal).byteValue() == Byte.MIN_VALUE) {
            return false;
        } else if (attributeVal instanceof Long && ((Long) attributeVal).longValue() == Long.MIN_VALUE) {
            return false;
        }
        return true;
    }

    /**
     * @return src with all occurrences of "from" replaced with "to".
     */

    public static String replaceOccurrences(String src, String from, String to) {
        // a little optimization: don't bother with strings that don't
        // have any occurrences to replace.
        if (-1 == src.indexOf(from)) {
            return src;
        }
        StringBuffer result = new StringBuffer(src.length());
        StringTokenizer toker = new StringTokenizer(src, from, true);
        String curToken = null;
        while (toker.hasMoreTokens()) {
            // if the current token is a delimiter, replace it with "to"
            if ((curToken = toker.nextToken()).equals(from)) {
                result.append(to);
            } else {
                // it's not a delimiter, just output it.
                result.append(curToken);
            }
        }

        return result.toString();
    }

    public static Object evaluateVBExpression(String expression) {
        if (expression == null || (!isVBExpression(expression))) {
            return expression;
        }
        FacesContext context = FacesContext.getCurrentInstance();
        Object result = context.getApplication().createValueBinding(expression).getValue(context);
        return result;

    }

    public static ValueBinding getValueBinding(String valueRef) {
        ValueBinding vb = null;
        // Must parse the value to see if it contains more than
        // one expression
        FacesContext context = FacesContext.getCurrentInstance();
        vb = context.getApplication().createValueBinding(valueRef);
        return vb;
    }

    public static MethodBinding createConstantMethodBinding(String outcome) {
        return new ConstantMethodBinding(outcome);
    }

    /**
     * This method will return a <code>SessionMap</code> for the current
     * <code>FacesContext</code>.  If the <code>FacesContext</code> argument
     * is null, then one is determined by <code>FacesContext.getCurrentInstance()</code>.
     * The <code>SessionMap</code> will be created if it is null.
     *
     * @param context the FacesContext
     * @return Map The <code>SessionMap</code>
     */
    public static Map getSessionMap(FacesContext context) {
        if (context == null) {
            context = FacesContext.getCurrentInstance();
        }
        return context.getExternalContext().getSessionMap();
    }

    public static Converter getConverterForClass(Class converterClass, FacesContext facesContext) {
        if (converterClass == null) {
            return null;
        }
        try {
            Application application = facesContext.getApplication();
            return (application.createConverter(converterClass));
        } catch (Exception e) {
            return (null);
        }
    }

    public static Converter getConverterForIdentifer(String converterId, FacesContext facesContext) {
        if (converterId == null) {
            return null;
        }
        try {
            Application application = facesContext.getApplication();
            return (application.createConverter(converterId));
        } catch (Exception e) {
            return (null);
        }
    }

    /**
     * <p>Return the single {@link ExpressionEvaluator} instance.</p>
     *
     * @return an ExpressionEvaluator
     */
    public static ExpressionEvaluator getExpressionEvaluator() {
        return FACES_EXPRESSION_EVALUATOR;
    }

    /*
     * Determine whether String is a value binding expression or not.
     */
    public static boolean isVBExpression(String expression) {
        if (null == expression) {
            return false;
        }
        int start = 0;
        //check to see if attribute has an expression
        if (((start = expression.indexOf("#{")) != -1) && (start < expression.indexOf('}'))) {
            return true;
        }
        return false;
    }

    /*
     * Determine whether String is a mixed value binding expression or not.
     */
    public static boolean isMixedVBExpression(String expression) {
        if (null == expression) {
            return false;
        }
        int start = 0;
        // if it doesn't start and end with delimiters
        if (!(expression.startsWith("#{") && expression.endsWith("}"))) {
            // see if it has some inside.
            return isVBExpression(expression);
        }
        return false;
    }

    public static StateManager getStateManager(FacesContext context) throws FacesException {
        return (context.getApplication().getStateManager());
    }

    public static ViewHandler getViewHandler(FacesContext context) throws FacesException {
        // Get Application instance
        Application application = context.getApplication();
        Util.doAssert(application != null);

        // Get the ViewHandler
        ViewHandler viewHandler = application.getViewHandler();
        Util.doAssert(viewHandler != null);

        return viewHandler;
    }

    public static ResponseStateManager getResponseStateManager(FacesContext context, String renderKitId)
            throws FacesException {
        RenderKit renderKit = context.getRenderKit();

        if (renderKit == null) {
            // check request scope for a RenderKitFactory implementation
            Map requestMap = context.getExternalContext().getRequestMap();
            RenderKitFactory factory = (RenderKitFactory) requestMap.get(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 IllegalStateException();
                } else {
                    requestMap.put(RENDER_KIT_IMPL_REQ, factory);
                }
                renderKit = factory.getRenderKit(context, renderKitId);
            }
        }

        return renderKit.getResponseStateManager();
    }

    public static boolean componentIsDisabledOnReadonly(UIComponent component) {
        Object disabledOrReadonly = null;
        boolean result = false;
        if (null != (disabledOrReadonly = component.getAttributes().get("disabled"))) {
            if (disabledOrReadonly instanceof String) {
                result = ((String) disabledOrReadonly).equalsIgnoreCase("true");
            } else {
                result = disabledOrReadonly.equals(Boolean.TRUE);
            }
        }
        if ((result == false) && null != (disabledOrReadonly = component.getAttributes().get("readonly"))) {
            if (disabledOrReadonly instanceof String) {
                result = ((String) disabledOrReadonly).equalsIgnoreCase("true");
            } else {
                result = disabledOrReadonly.equals(Boolean.TRUE);
            }
        }

        return result;
    }

    public static Object createInstance(String className) {
        return createInstance(className, null, null);
    }

    public static Object createInstance(String className, Class rootType, Object root) {
        Class clazz = null;
        Object returnObject = null;
        if (className != null) {
            try {
                clazz = Util.loadClass(className, returnObject);
                if (clazz != null) {
                    // Look for an adapter constructor if we've got
                    // an object to adapt
                    if ((rootType != null) && (root != null)) {
                        try {
                            Class[] parameterTypes = new Class[] { rootType };
                            Constructor construct = clazz.getConstructor(parameterTypes);
                            Object[] parameters = new Object[] { root };
                            returnObject = construct.newInstance(parameters);
                        } catch (NoSuchMethodException nsme) {
                            // OK - there's no adapter constructor
                        }
                    }

                    if (returnObject == null) {
                        returnObject = clazz.newInstance();
                    }
                }
            } catch (Exception e) {
                Object[] params = new Object[1];
                params[0] = className;
                String msg = Util.getExceptionMessageString(Util.CANT_INSTANTIATE_CLASS_ERROR_MESSAGE_ID, params);
                if (log.isErrorEnabled()) {
                    log.error(msg + ":" + className + ":exception:" + e.getMessage());
                }
            }
        }
        return returnObject;
    }

    // W3C XML specification refers to IETF RFC 1766 for language code
    // structure, therefore the value for the xml:lang attribute should
    // be in the form of language or language-country or
    // language-country-variant.

    public static Locale getLocaleFromString(String localeStr) throws IllegalArgumentException {
        // length must be at least 2.
        if (null == localeStr || localeStr.length() < 2) {
            throw new IllegalArgumentException("Illegal locale String: " + localeStr);
        }

        Locale result = null;
        String lang = null, country = null, variant = null;
        char[] seps = { '-', '_' };
        int i = 0, j = 0;

        // to have a language, the length must be >= 2
        if ((localeStr.length() >= 2) && (-1 == (i = indexOfSet(localeStr, seps, 0)))) {
            // we have only Language, no country or variant
            if (2 != localeStr.length()) {
                throw new IllegalArgumentException("Illegal locale String: " + localeStr);
            }
            lang = localeStr.toLowerCase();
        }

        // we have a separator, it must be either '-' or '_'
        if (-1 != i) {
            lang = localeStr.substring(0, i);
            // look for the country sep.
            // to have a country, the length must be >= 5
            if ((localeStr.length() >= 5) && (-1 == (j = indexOfSet(localeStr, seps, i + 1)))) {
                // no further separators, length must be 5
                if (5 != localeStr.length()) {
                    throw new IllegalArgumentException("Illegal locale String: " + localeStr);
                }
                country = localeStr.substring(i + 1);
            }
            if (-1 != j) {
                country = localeStr.substring(i + 1, j);
                // if we have enough separators for language, locale,
                // and variant, the length must be >= 8.
                if (localeStr.length() >= 8) {
                    variant = localeStr.substring(j + 1);
                } else {
                    throw new IllegalArgumentException("Illegal locale String: " + localeStr);
                }
            }
        }
        if (null != variant && null != country && null != lang) {
            result = new Locale(lang, country, variant);
        } else if (null != lang && null != country) {
            result = new Locale(lang, country);
        } else if (null != lang) {
            result = new Locale(lang, "");
        }
        return result;
    }

    /**
     * @return starting at <code>fromIndex</code>, the index of the
     *         first occurrence of any substring from <code>set</code> in
     *         <code>toSearch</code>, or -1 if no such match is found
     */

    public static int indexOfSet(String str, char[] set, int fromIndex) {
        int result = -1;
        char[] toSearch = str.toCharArray();
        for (int i = fromIndex, len = toSearch.length; i < len; i++) {
            for (int j = 0, innerLen = set.length; j < innerLen; j++) {
                if (toSearch[i] == set[j]) {
                    result = i;
                    break;
                }
            }
            if (-1 != result) {
                break;
            }
        }
        return result;
    }

    public static String stripBracketsIfNecessary(String expression) throws ReferenceSyntaxException {
        Util.doAssert(null != expression);
        int len = 0;
        // look for invalid expressions
        if ('#' == expression.charAt(0)) {
            if ('{' != expression.charAt(1)) {
                throw new ReferenceSyntaxException(
                        Util.getExceptionMessageString(Util.INVALID_EXPRESSION_ID, new Object[] { expression }));
            }
            if ('}' != expression.charAt((len = expression.length()) - 1)) {
                throw new ReferenceSyntaxException(
                        Util.getExceptionMessageString(Util.INVALID_EXPRESSION_ID, new Object[] { expression }));
            }
            expression = expression.substring(2, len - 1);
        }
        return expression;
    }

    //
    // General Methods
    //

    private static boolean assertEnabled = true;

    public static void doAssert(boolean cond) throws FacesException {
        if (assertEnabled && !cond) {
            throw new FacesException(getExceptionMessageString(ASSERTION_FAILED_ID));
        }
    }

    public static void parameterNonNull(Object param) throws FacesException {
        if (null == param) {
            throw new FacesException(getExceptionMessageString(NULL_PARAMETERS_ERROR_MESSAGE_ID));
        }
    }

    public static void parameterNonEmpty(String param) throws FacesException {
        if (null == param || 0 == param.length()) {
            throw new FacesException(getExceptionMessageString(EMPTY_PARAMETER_ID));
        }
    }

    /**
     * <p>This method is used by the ManagedBeanFactory to ensure that
     * properties set by an expression point to an object with an
     * accepted lifespan.</p>
     *
     * <p>get the scope of the expression. Return <code>null</code> if
     * it isn't scoped</p> 
     *
     * <p>For example, the expression:
     * <code>sessionScope.TestBean.one</code> should return "session" 
     * as the scope.</p>
     *
     * @param valueBinding the expression
     *
     * @param outString an allocated String Array into which we put the
     * first segment.
     *
     * @return the scope of the expression
     */
    public static String getScope(String valueBinding, String[] outString) throws ReferenceSyntaxException {
        if (valueBinding == null || 0 == valueBinding.length()) {
            return null;
        }
        valueBinding = stripBracketsIfNecessary(valueBinding);

        int segmentIndex = getFirstSegmentIndex(valueBinding);

        //examine first segment and see if it is a scope
        String identifier = valueBinding;

        if (segmentIndex > 0) {
            //get first segment designated by a "." or "["
            identifier = valueBinding.substring(0, segmentIndex);
        }

        //check to see if the identifier is a named scope.

        FacesContext context = FacesContext.getCurrentInstance();
        ExternalContext ec = context.getExternalContext();

        if (null != outString) {
            outString[0] = identifier;
        }
        if (identifier.equalsIgnoreCase(RIConstants.REQUEST_SCOPE)) {
            return RIConstants.REQUEST;
        }
        if (identifier.equalsIgnoreCase(RIConstants.SESSION_SCOPE)) {
            return RIConstants.SESSION;
        }
        if (identifier.equalsIgnoreCase(RIConstants.APPLICATION_SCOPE)) {
            return RIConstants.APPLICATION;
        }

        // handle implicit objects
        if (identifier.equalsIgnoreCase(RIConstants.INIT_PARAM_IMPLICIT_OBJ)) {
            return RIConstants.APPLICATION;
        }
        if (identifier.equalsIgnoreCase(RIConstants.COOKIE_IMPLICIT_OBJ)) {
            return RIConstants.REQUEST;
        }
        if (identifier.equalsIgnoreCase(RIConstants.FACES_CONTEXT_IMPLICIT_OBJ)) {
            return RIConstants.REQUEST;
        }
        if (identifier.equalsIgnoreCase(RIConstants.HEADER_IMPLICIT_OBJ)) {
            return RIConstants.REQUEST;
        }
        if (identifier.equalsIgnoreCase(RIConstants.HEADER_VALUES_IMPLICIT_OBJ)) {
            return RIConstants.REQUEST;
        }
        if (identifier.equalsIgnoreCase(RIConstants.PARAM_IMPLICIT_OBJ)) {
            return RIConstants.REQUEST;
        }
        if (identifier.equalsIgnoreCase(RIConstants.PARAM_VALUES_IMPLICIT_OBJ)) {
            return RIConstants.REQUEST;
        }
        if (identifier.equalsIgnoreCase(RIConstants.VIEW_IMPLICIT_OBJ)) {
            return RIConstants.REQUEST;
        }

        //No scope was provided in the expression so check for the 
        //expression in all of the scopes. The expression is the first 
        //segment.

        if (ec.getRequestMap().get(identifier) != null) {
            return RIConstants.REQUEST;
        }
        if (Util.getSessionMap(context).get(identifier) != null) {
            return RIConstants.SESSION;
        }
        if (ec.getApplicationMap().get(identifier) != null) {
            return RIConstants.APPLICATION;
        }

        //not present in any scope
        return null;
    }

    /**
     * @result a List of expressions from the expressionString
     *
     * @param expressionString the expression string, with delimiters
     * intact.
     */

    public static List getExpressionsFromString(String expressionString) throws ReferenceSyntaxException {
        if (null == expressionString) {
            return Collections.EMPTY_LIST;
        }
        List result = new ArrayList();
        int i, j, len = expressionString.length(), cur = 0;
        while (cur < len && -1 != (i = expressionString.indexOf("#{", cur))) {
            if (-1 == (j = expressionString.indexOf("}", i + 2))) {
                throw new ReferenceSyntaxException(Util.getExceptionMessageString(Util.INVALID_EXPRESSION_ID,
                        new Object[] { expressionString }));
            }
            cur = j + 1;
            result.add(expressionString.substring(i, cur));
        }
        return result;
    }

    /**
     * <p/>
     * The the first segment of a String tokenized by a "." or "["
     *
     * @return index of the first occurrence of . or [
     */
    private static int getFirstSegmentIndex(String valueBinding) {
        int segmentIndex = valueBinding.indexOf(".");
        int bracketIndex = valueBinding.indexOf("[");

        //there is no "." in the valueBinding so take the bracket value
        if (segmentIndex < 0) {
            segmentIndex = bracketIndex;
        } else {
            //if there is a bracket proceed
            if (bracketIndex > 0) {
                //if the bracket index is before the "." then
                //get the bracket index
                if (segmentIndex > bracketIndex) {
                    segmentIndex = bracketIndex;
                }
            }
        }
        return segmentIndex;
    }

    /**
     * <p>Leverage the Throwable.printStackTrace() method to produce a
     * String version of the stack trace, with a "\n" before each
     * line.</p>
     *
     * @return the String representation ofthe stack trace obtained by
     * calling getStackTrace() on the passed in exception.  If null is
     * passed in, we return the empty String.
     */

    public static String getStackTraceString(Throwable e) {
        if (null == e) {
            return "";
        }

        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        PrintStream ps = new PrintStream(baos);
        e.printStackTrace(ps);
        return baos.toString();
    }

} // end of class Util