Package com.sun.faces.renderkit

Source Code of com.sun.faces.renderkit.RenderKitUtils

/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 1997-2007 Sun Microsystems, Inc. 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.html
* or glassfish/bootstrap/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 glassfish/bootstrap/legal/LICENSE.txt.
* Sun designates this particular file as subject to the "Classpath" exception
* as provided by Sun in the GPL Version 2 section of the License file that
* accompanied this code.  If applicable, add the following below the License
* Header, with the fields enclosed by brackets [] replaced by your own
* identifying information: "Portions Copyrighted [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.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.Writer;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
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.component.UIComponent;
import javax.faces.component.UIComponentBase;
import javax.faces.component.UISelectItem;
import javax.faces.component.UISelectItems;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import javax.faces.model.SelectItem;
import javax.faces.render.RenderKit;
import javax.faces.render.RenderKitFactory;
import javax.faces.render.ResponseStateManager;

import com.sun.faces.RIConstants;
import com.sun.faces.config.WebConfiguration;
import static com.sun.faces.config.WebConfiguration.BooleanWebContextInitParameter;
import com.sun.faces.renderkit.html_basic.HtmlBasicRenderer.Param;
import com.sun.faces.util.FacesLogger;
import com.sun.faces.util.MessageUtils;
import com.sun.faces.util.Util;
import com.sun.faces.util.RequestStateManager;

/**
* <p>A set of utilities for use in {@link RenderKit}s.</p>
*/
public class RenderKitUtils {

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

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

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

    /**
     * <p>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 </p>
     */
    private final static int MAX_CONTENT_TYPE_PARTS = 4;
                                                                                                                        
    /**
     * The character that is used to delimit content types
     * in an accept String.</p>
     */
    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 </p>
     */
    private final static String CONTENT_TYPE_SUBTYPE_DELIMITER = "/";

    /**
     * <p>JavaScript to be rendered when a commandLink is used.
     * This may be expaned to include other uses.</p>
     */
    private static final String SUN_JSF_JS = RIConstants.FACES_PREFIX + "sunJsfJs";


    /**
     * This represents the base package that can leverage the
     * <code>attributesThatAreSet</code> 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


    /**
     * <p>Return the {@link RenderKit} for the current request.</p>
     * @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());

    }


    /**
     * <p>Obtain and return the {@link ResponseStateManager} for
     * the specified #renderKitId.</p>
     *
     * @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 <code>ResponseStateManager</code>
     */
    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 IllegalStateException();
                } else {
                    RequestStateManager.set(context,
                                            RequestStateManager.RENDER_KIT_IMPL_REQ,
                                            factory);
                }
                renderKit = factory.getRenderKit(context, renderKitId);
            }
        }
        return renderKit.getResponseStateManager();

    }
   
    /**
     * <p>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 <code>Iterator</code> is returned.</p>
     *
     * @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 <code>context</code>
     *                                  is <code>null</code>
     * @return a List of the select items for the specified component
     */
    public static List<SelectItem> getSelectItems(FacesContext context,
                                                  UIComponent component) {

        if (context == null) {           
            throw new IllegalArgumentException(
                MessageUtils.getExceptionMessageString(
                    MessageUtils.NULL_PARAMETERS_ERROR_MESSAGE_ID, "context"));
        }
       
        ArrayList<SelectItem> list = new ArrayList<SelectItem>();
        for (UIComponent kid : component.getChildren()) {
            if (kid instanceof UISelectItem) {
                UISelectItem item = (UISelectItem) kid;
                Object value = item.getValue();
               
                if (value == null) {
                    list.add(new SelectItem(item.getItemValue(),
                                            item.getItemLabel(),
                                            item.getItemDescription(),
                                            item.isItemDisabled(),
                                            item.isItemEscaped()));
                } else if (value instanceof SelectItem) {
                    list.add((SelectItem) value);
                } else {
                    throw new IllegalArgumentException(
                        MessageUtils.getExceptionMessageString(
                            MessageUtils.VALUE_NOT_SELECT_ITEM_ID,
                            component.getId(),
                            value.getClass().getName()));
                }
            } else if (kid instanceof UISelectItems) {
                Object value = ((UISelectItems) kid).getValue();
                if (value instanceof SelectItem) {
                    list.add((SelectItem) value);
                } else if (value instanceof SelectItem[]) {
                    SelectItem[] items = (SelectItem[]) value;
                    // we manually copy the elements so that the list is
                    // modifiable.  Arrays.asList() returns a non-mutable
                    // list.
                    //noinspection ManualArrayToCollectionCopy
                    for (SelectItem item : items) {
                        list.add(item);
                    }
                } else if (value instanceof Collection) {
                    for (Object element : ((Collection) value)) {
                        if (SelectItem.class.isInstance(element)) {
                            list.add((SelectItem) element);
                        } else {
                            throw new IllegalArgumentException(
                                  MessageUtils.getExceptionMessageString(
                                        MessageUtils.VALUE_NOT_SELECT_ITEM_ID,
                                        component.getId(),
                                        value.getClass().getName()));
                        }
                    }
                } else if (value instanceof Map) {
                    Map optionMap = (Map) value;
                    for (Object o : optionMap.entrySet()) {
                        Entry entry = (Entry) o;
                        Object key = entry.getKey();
                        Object val = entry.getValue();
                        if (key == null || val == null) {
                            continue;
                        }
                        list.add(new SelectItem(val,
                                                key.toString()));
                    }
                } else {
                    throw new IllegalArgumentException(
                          MessageUtils.getExceptionMessageString(
                            MessageUtils.CHILD_NOT_OF_EXPECTED_TYPE_ID,
                            "UISelectItem/UISelectItems",
                            component.getFamily(),
                            component.getId(),
                            value != null ? value.getClass().getName() : "null"));
                }
            }
        }
        return (list);

    }


    /**
     * <p>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>
     *
     * @param writer writer the {@link javax.faces.context.ResponseWriter} to be used when writing
     *  the attributes
     * @param component the component
     * @param attributes an array off attributes to be processed
     * @throws IOException if an error occurs writing the attributes
     */
    public static void renderPassThruAttributes(ResponseWriter writer,
                                                UIComponent component,
                                                String[] attributes)
    throws IOException {

        assert (null != writer);
        assert (null != component);

        Map<String, Object> attrMap = component.getAttributes();

        // PENDING - think anyone would run the RI using another implementation
        // of the jsf-api?  If they did, then this would fall apart.  That
        // scenario seems extremely unlikely.
        if (canBeOptimized(component)) {
            //noinspection unchecked
            List<String> setAttributes = (List<String>)
              component.getAttributes().get(ATTRIBUTES_THAT_ARE_SET_KEY);
            if (setAttributes != null) {
                renderPassThruAttributesOptimized(writer,
                                                  component,
                                                  attributes,
                                                  setAttributes);
            }
        } else {
            // this block should only be hit by custom components leveraging
            // the RI's rendering code.  We make no assumptions and loop through
            // all known attributes.
            boolean isXhtml =
                  RIConstants.XHTML_CONTENT_TYPE.equals(writer.getContentType());
            for (String attrName : attributes) {

                Object value =
                      attrMap.get(attrName);
                if (value != null && shouldRenderAttribute(value)) {
                    writer.writeAttribute(prefixAttribute(attrName, isXhtml),
                                          value,
                                          attrName);
                }
            }

        }
    }


    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;
        }
       
    }


    /**
     * <p>Renders the attributes from {@link #BOOLEAN_ATTRIBUTES}
     * using <code>XHMTL</code> semantics (i.e., disabled="disabled").</p>
     *
     * @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);

        Map attrMap = component.getAttributes();
        for (String attrName : BOOLEAN_ATTRIBUTES) {
            Object val = attrMap.get(attrName);
            if (val == null) {
                continue;
            }

            if (Boolean.valueOf(val.toString())) {
                writer.writeAttribute(attrName,
                                      true,
                                      attrName);
            }
        }

    }

    /**
     * <p>Given an accept String from the client, and a <code>String</code>
     * 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 <code>null</code><code>null</code> is returned.</p>
     *
     * @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 <code>String</code>
     */
    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 <code>true</code> if the content type is a known XML-based
     *  content type, otherwise, <code>false</code>
     */
    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 <code>true</code> if the component is within the
     *  <code>javax.faces.component</code> or <code>javax.faces.component.html</code>
     *  packages, otherwise return <code>false</code>
     */
    private static boolean canBeOptimized(UIComponent component) {

        String name = component.getClass().getName();
        return (name != null && name.startsWith(OPTIMIZED_PACKAGE));

    }


    /**
     * <p>For each attribute in <code>setAttributes</code>, perform a binary
     * search against the array of <code>knownAttributes</code>  If a match is found
     * and the value is not <code>null</code>, render the attribute.
     * @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 <code>List</code> of attributes that have been set
     *  on the provided component
     * @throws IOException if an error occurs during the write
     */
    private static void renderPassThruAttributesOptimized(ResponseWriter writer,
                                                          UIComponent component,
                                                          String[] knownAttributes,
                                                          List<String> setAttributes)
    throws IOException {

        String[] attributes = setAttributes.toArray(new String[setAttributes.size()]);
        Arrays.sort(attributes);
        boolean isXhtml =
              RIConstants.XHTML_CONTENT_TYPE.equals(writer.getContentType());
        Map<String, Object> attrMap = component.getAttributes();
        for (String name : attributes) {
            if (Arrays.binarySearch(knownAttributes, name) >= 0) {
                Object value =
                      attrMap.get(name);
                if (value != null && shouldRenderAttribute(value)) {
                    writer.writeAttribute(prefixAttribute(name, isXhtml),
                                          value,
                                          name);
                }
            }
        }

    }


    /**
     * <p>Determines if an attribute should be rendered based on the
     * specified #attributeVal.</p>
     *
     * @param attributeVal the attribute value
     * @return <code>true</code> 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;

    }

    /**
     * <p>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.</p>
     *
     * @param accept An accept <code>String</code>
     * @return an two dimensional array containing content-type/quality info
     */
    private static String[][] buildTypeArrayFromString(String accept) {
        //String[][] arrayAccept = new String[MAX_CONTENT_TYPES][MAX_CONTENT_TYPE_PARTS];
        // 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;

        // Parse "types"       
        String[] types = Util.split(accept, CONTENT_TYPE_DELIMITER);
        String[][] arrayAccept = new String[types.length][MAX_CONTENT_TYPE_PARTS];
        int index = -1;
        for (int i=0; i<types.length; i++) {
            String token = types[i].trim();
            index += 1;
            // Check to see if our accept string contains the delimiter that is used
            // to add uniqueness to a type/subtype, and/or delimits a qualifier value:
            //    Example: text/html;level=1,text/html;level=2; q=.5
            if (token.contains(";")) {               
                String[] typeParts = Util.split(token, ";");
                typeSubType = new StringBuilder(typeParts[0].trim());
                for (int j=1; j<typeParts.length; j++) {
                    quality = "not set";
                    token = typeParts[j].trim();
                    // if "level" is present, make sure it gets included in the "type/subtype"
                    if (token.contains("level")) {
                        typeSubType.append(';').append(token);
                        String[] levelParts = Util.split(token, "=");                       
                        level = levelParts[0].trim();
                        if (level.equalsIgnoreCase("level")) {
                            level = levelParts[1].trim();
                        }
                    } else {
                        quality = token;
                        String[] qualityParts = Util.split(quality, "=");                      
                        quality = qualityParts[0].trim();
                        if (quality.equalsIgnoreCase("q")) {
                            quality = qualityParts[1].trim();
                            break;
                        } else {
                            quality = "not set"; // to identifiy that no quality was supplied
                        }
                    }
                }
            } else {
                typeSubType = new StringBuilder(token);
                quality = "not set"; // to identifiy that no quality was supplied
            }
            // now split type and subtype
            if (typeSubType.indexOf(CONTENT_TYPE_SUBTYPE_DELIMITER) >= 0) {              
                String[] typeSubTypeParts = Util.split(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);
    }

    /**
     * <p>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.</p>
     *
     * @param clientContentTypes An <code>array</code> of accept <code>String</code>
     * information for the client built from @{link #buildTypeArrayFromString}.
     * @param serverSupportedContentTypes An <code>array</code> of accept <code>String</code>
     * information for the server supported types built from @{link #buildTypeArrayFromString}.
     * @param preferredContentType An <code>array</code> of preferred content type information.
     * @return An <code>array</code> 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) {

        // result array
        List<String[]> resultList = new ArrayList<String[]>(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;
    }

    /**
     * <p>Replaces all occurrences of <code>-</code> with <code>$_</code>.</p>
     *
     * @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("-", "$_");
    }


    /**
     * <p>Renders the Javascript necessary to add and remove request
     * parameters to the current form.</p>
     * @param writer the <code>ResponseWriter</code>
     * @param context the <code>FacesContext</code> for the current request
     * @throws java.io.IOException if an error occurs writing to the response
     */
    public static void renderFormInitScript(ResponseWriter writer,
                                            FacesContext context)
          throws IOException {
        WebConfiguration webConfig =
              WebConfiguration.getInstance(context.getExternalContext());

        if (webConfig.isOptionEnabled(BooleanWebContextInitParameter.ExternalizeJavaScript)) {
            // PENDING
            // We need to look into how to make this work in a portlet environment.
            // For the time being, this feature will need to be disabled when running
            // in a portlet.
            String mapping = Util.getFacesMapping(context);
            String uri;
            if ((mapping != null) && (Util.isPrefixMapped(mapping))) {
                uri = mapping + '/' + RIConstants.SUN_JSF_JS_URI;
            } else {
                uri = '/' + RIConstants.SUN_JSF_JS_URI + mapping;
            }
            writer.write('\n');
            writer.startElement("script", null);
            writer.writeAttribute("type", "text/javascript", null);
            writer.writeAttribute("src",
                                  context.getExternalContext()
                                        .getRequestContextPath() + uri,
                                  null);
            writer.endElement("script");
            writer.write("\n");
        } else {          
            writer.write('\n');
            writer.startElement("script", null);
            writer.writeAttribute("type", "text/javascript", null);
            writer.writeAttribute("language", "Javascript", null);          
            writeSunJS(context, writer);
            writer.endElement("script");
            writer.write("\n");
        }
    }

    /**
     * <p>Returns a string that can be inserted into the <code>onclick</code>
     * handler of a command.  This string will add all request parameters
     * as well as the client ID of the activated command to the form as
     * hidden input parameters, update the target of the link if necessary,
     * and handle the form submission.  The content of {@link #SUN_JSF_JS}
     * must be rendered prior to using this method.</p>
     * @param formClientId the client ID of the form
     * @param commandClientId the client ID of the command
     * @param target the link target
     * @param params the nested parameters, if any @return a String suitable for the <code>onclick</code> handler
     *  of a command
     * @return the default <code>onclick</code> JavaScript for the default
     *  command link component
     */
    public static String getCommandLinkOnClickScript(String formClientId,
                                                     String commandClientId,
                                                     String target,
                                                     Param[] params) {

        StringBuilder sb = new StringBuilder(256);   
        sb.append("if(typeof jsfcljs == 'function'){jsfcljs(document.getElementById('");
        sb.append(formClientId);       
        sb.append("'),{'");
        sb.append(commandClientId).append("':'").append(commandClientId);
        for (Param param : params) {
            String pn = param.name;
            if (pn != null && pn.length() != 0) {
                String pv = param.value;
                sb.append("','");
                sb.append(pn.replace("'", "\\\'"));
                sb.append("':'");
                if (pv != null && pv.length() != 0) {
                    sb.append(pv.replace("'", "\\\'"));
                }
            }
        }
        sb.append("'},'");
        sb.append(target);
        sb.append("');}return false");

        return sb.toString();
       
    }


    /**
     * <p>This is a utility method for compressing multi-lined javascript.
     * In the case of {@link #SUN_JSF_JS} it offers about a 47% decrease
     * in length.</p>
     *
     * <p>For our purposes, compression is just trimming each line and
     * then writing it out.  It's pretty simplistic, but it works.</p>
     *
     * @param JSString the string to compress
     * @return the compressed string
     */
    public static char[] compressJS(String JSString) {

        BufferedReader reader = new BufferedReader(new StringReader(JSString));
        StringWriter writer = new StringWriter(1024);         
        try {
            for (String line = reader.readLine();
                 line != null;
                 line = reader.readLine()) {

                line = line.trim();
                writer.write(line);
            }
            return writer.toString().toCharArray();
        } catch (IOException ioe) {
            // won't happen
        }
        return null;

    }


    /**
     * <p>Return the implementation JavaScript.  If compression
     * is enabled, the result will be compressed.</p>
     *
     * @param context - the <code>FacesContext</code> for the current request
     * @param writer - the <code>Writer</code> to write the JS to
     * @throws IOException if the JavaScript cannot be written
     *
     */
    public static void writeSunJS(FacesContext context, Writer writer)
    throws IOException {  
        writer.write((char[]) context.getExternalContext().getApplicationMap()
              .get(SUN_JSF_JS));
    }
   
   
    // --------------------------------------------------------- Private Methods


    /**
     * <p>Loads the contents of the sunjsf.js file into memory removing any
     * comments/empty lines it encoutners, and, if enabled, compressing the
     * result.</p>  This method should only be called when the application is
     * being initialized.
     * @param extContext the ExternalContext for this application
     */
    public synchronized static void loadSunJsfJs(ExternalContext extContext) {
        Map<String, Object> appMap =
             extContext.getApplicationMap();
        char[] sunJsfJs;

        BufferedReader reader = null;
        try {
            // Don't use Util.getCurrentLoader().  This JS resource should
            // be available from the same classloader that loaded RenderKitUtils.
            // Doing so allows us to be more OSGi friendly.
            URL url = RenderKitUtils.class.getClassLoader()
                  .getResource("com/sun/faces/sunjsf.js");
            if (url == null) {
                LOGGER.severe(
                     "jsf.renderkit.util.cannot_load_js");
                return;
            }
            URLConnection conn = url.openConnection();
            conn.setUseCaches(false);
            InputStream input = conn.getInputStream();
            reader = new BufferedReader(
                 new InputStreamReader(input));
            StringBuilder builder = new StringBuilder(128);
            for (String line = reader.readLine();
                 line != null;
                 line = reader.readLine()) {

                String temp = line.trim();
                if (temp.length() == 0
                     || temp.startsWith("/*")
                     || temp.startsWith("*")
                     || temp.startsWith("*/")
                     || temp.startsWith("//")) {
                    continue;
                }
                builder.append(line).append('\n');
            }
            builder.deleteCharAt(builder.length() - 1);
            if (WebConfiguration
                 .getInstance(extContext)
                 .isOptionEnabled(BooleanWebContextInitParameter.CompressJavaScript)) {
                sunJsfJs = compressJS(builder.toString());
            } else {
                sunJsfJs = builder.toString().toCharArray();
            }
            appMap.put(SUN_JSF_JS, sunJsfJs);
        } catch (IOException ioe) {
            LOGGER.log(Level.SEVERE,
                 "jsf.renderkit.util.cannot_load_js",
                 ioe);
        } finally {
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException ioe) {
                    // ignore
                }
            }
        }
    }
                          
} // END RenderKitUtils
TOP

Related Classes of com.sun.faces.renderkit.RenderKitUtils

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.