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

com.sun.faces.util.Util Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2021, 2023 Contributors to Eclipse Foundation.
 * Copyright (c) 1997, 2021 Oracle and/or its affiliates. All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v. 2.0, which is available at
 * http://www.eclipse.org/legal/epl-2.0.
 *
 * This Source Code may also be made available under the following Secondary
 * Licenses when the conditions for such availability set forth in the
 * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
 * version 2 with the GNU Classpath Exception, which is available at
 * https://www.gnu.org/software/classpath/license.html.
 *
 * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
 */

// Util.java

package com.sun.faces.util;

import static com.sun.faces.RIConstants.CDI_BEAN_MANAGER;
import static com.sun.faces.RIConstants.FACELETS_ENCODING_KEY;
import static com.sun.faces.RIConstants.FACES_SERVLET_MAPPINGS;
import static com.sun.faces.RIConstants.FACES_SERVLET_REGISTRATION;
import static com.sun.faces.RIConstants.NO_VALUE;
import static com.sun.faces.util.MessageUtils.ILLEGAL_ATTEMPT_SETTING_APPLICATION_ARTIFACT_ID;
import static com.sun.faces.util.MessageUtils.NAMED_OBJECT_NOT_FOUND_ERROR_MESSAGE_ID;
import static com.sun.faces.util.MessageUtils.NULL_PARAMETERS_ERROR_MESSAGE_ID;
import static com.sun.faces.util.MessageUtils.NULL_VIEW_ID_ERROR_MESSAGE_ID;
import static com.sun.faces.util.MessageUtils.getExceptionMessageString;
import static jakarta.faces.application.ViewHandler.CHARACTER_ENCODING_KEY;
import static java.lang.Character.isDigit;
import static java.util.Collections.emptyList;
import static java.util.logging.Level.FINE;
import static java.util.logging.Level.FINEST;
import static java.util.logging.Level.SEVERE;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.JarURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Pattern;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;

import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.xml.XMLConstants;
import javax.xml.namespace.NamespaceContext;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.transform.TransformerFactory;
import javax.xml.validation.SchemaFactory;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;

import com.sun.faces.RIConstants;
import com.sun.faces.application.ApplicationAssociate;
import com.sun.faces.config.WebConfiguration;
import com.sun.faces.config.manager.FacesSchema;
import com.sun.faces.facelets.component.UIRepeat;
import com.sun.faces.io.FastStringWriter;

import jakarta.el.ValueExpression;
import jakarta.enterprise.inject.spi.BeanManager;
import jakarta.enterprise.inject.spi.CDI;
import jakarta.enterprise.inject.spi.el.ELAwareBeanManager;
import jakarta.faces.FacesException;
import jakarta.faces.application.Application;
import jakarta.faces.application.ProjectStage;
import jakarta.faces.application.StateManager;
import jakarta.faces.application.ViewHandler;
import jakarta.faces.component.Doctype;
import jakarta.faces.component.NamingContainer;
import jakarta.faces.component.UIComponent;
import jakarta.faces.component.UIData;
import jakarta.faces.component.UINamingContainer;
import jakarta.faces.component.UIViewRoot;
import jakarta.faces.context.ExternalContext;
import jakarta.faces.context.FacesContext;
import jakarta.faces.convert.Converter;
import jakarta.faces.event.AbortProcessingException;
import jakarta.faces.render.ResponseStateManager;
import jakarta.faces.webapp.FacesServlet;
import jakarta.servlet.ServletContext;
import jakarta.servlet.ServletRegistration;
import jakarta.servlet.http.HttpServletMapping;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.MappingMatch;

/**
 * Util is a class ...
 *
 * Lifetime And Scope
 *
 */
public class Util {

    // Log instance for this class
    private static final Logger LOGGER = FacesLogger.APPLICATION.getLogger();

    // 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).

    /**
     * Flag that, when true, enables special behavior in Mojarra to enable unit testing.
     */
    private static boolean unitTestModeEnabled = false;

    /**
     * RegEx patterns
     */
    private static final String PATTERN_CACHE_KEY = RIConstants.FACES_PREFIX + "patternCache";

    private static final String CLIENT_ID_NESTED_IN_ITERATOR_PATTERN = "CLIENT_ID_NESTED_IN_ITERATOR_PATTERN";

    private static final String FACES_SERVLET_CLASS = FacesServlet.class.getName();

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

    private static Map getPatternCache(Map appMap) {
        @SuppressWarnings("unchecked")
        Map result = (Map) appMap.get(PATTERN_CACHE_KEY);
        if (result == null) {
            result = Collections.synchronizedMap(new LRUMap<>(15));
            appMap.put(PATTERN_CACHE_KEY, result);
        }

        return result;
    }

    private static Map getPatternCache(ServletContext sc) {
        @SuppressWarnings("unchecked")
        Map result = (Map) sc.getAttribute(PATTERN_CACHE_KEY);
        if (result == null) {
            result = Collections.synchronizedMap(new LRUMap<>(15));
            sc.setAttribute(PATTERN_CACHE_KEY, result);
        }

        return result;
    }

    private static Collection getFacesServletMappings(ServletContext servletContext) {
        // check servlet context during initialization to avoid ConfigureListener to call the servlet registration
        @SuppressWarnings("unchecked")
        Collection mappings = (Collection) servletContext.getAttribute(FACES_SERVLET_MAPPINGS);
        if (mappings != null) {
            return mappings;
        }

        ServletRegistration facesRegistration = getExistingFacesServletRegistration(servletContext);

        if (facesRegistration != null) {
            return facesRegistration.getMappings();
        }

        return emptyList();
    }

    private static ServletRegistration getExistingFacesServletRegistration(ServletContext servletContext) {
        Map existing = servletContext.getServletRegistrations();
        for (ServletRegistration registration : existing.values()) {
            if (FACES_SERVLET_CLASS.equals(registration.getClassName())) {
                return registration;
            }
        }

        return null;
    }

    public static Optional getFacesServletRegistration(FacesContext context) {
        Object unKnownContext = context.getExternalContext().getContext();
        if (unKnownContext instanceof ServletContext) {
            return Optional.of((ServletRegistration) ((ServletContext) unKnownContext).getAttribute(FACES_SERVLET_REGISTRATION));
        }

        return Optional.empty();
    }

    /**
     * 

* Convenience method for determining if the request associated with the specified FacesContext is a * PortletRequest submitted by the JSR-301 bridge. *

* * @param context the FacesContext associated with the request. */ public static boolean isPortletRequest(FacesContext context) { return context.getExternalContext().getRequestMap().get("javax.portlet.faces.phase") != null; } public static String generateCreatedBy(FacesContext facesContext) { String applicationContextPath = "unitTest"; try { applicationContextPath = facesContext.getExternalContext().getApplicationContextPath(); } catch (Throwable e) { // ignore } return applicationContextPath + " " + Thread.currentThread().toString() + " " + System.currentTimeMillis(); } /** *

* Factory method for creating the various Faces listener instances that may be referenced by type or * binding. *

*

* If binding is not null and the evaluation result is not null return that * instance. Otherwise try to instantiate an instances based on type. *

* * @param type the Listener type * @param binding a ValueExpression which resolves to a Listener instance * @return a Listener instance based off the provided type and binding */ public static Object getListenerInstance(ValueExpression type, ValueExpression binding) { FacesContext faces = FacesContext.getCurrentInstance(); Object instance = null; if (faces == null) { return null; } if (binding != null) { instance = binding.getValue(faces.getELContext()); } if (instance == null && type != null) { try { instance = ReflectionUtils.newInstance((String) type.getValue(faces.getELContext())); } catch (IllegalArgumentException | ReflectiveOperationException | SecurityException e) { throw new AbortProcessingException(e.getMessage(), e); } if (binding != null) { binding.setValue(faces.getELContext(), instance); } } return instance; } public static void setUnitTestModeEnabled(boolean enabled) { unitTestModeEnabled = enabled; } public static boolean isUnitTestModeEnabled() { return unitTestModeEnabled; } public static interface ThrowingBiConsumer { void accept(T t, U u) throws Exception; } private static void setFeature(ThrowingBiConsumer setter, F feature, Boolean flag) { try { setter.accept(feature, flag); } catch (Exception e) { throw new IllegalArgumentException("The feature '" + feature + "' is not supported by your XML processor.", e); } } private static void setPossiblyUnsupportedFeature(ThrowingBiConsumer setter, F feature, Boolean flag) { try { setFeature(setter, feature, flag); } catch (IllegalArgumentException e) { LOGGER.log(Level.FINE, e.getMessage(), e); } } public static TransformerFactory createTransformerFactory() { ClassLoader cl = Thread.currentThread().getContextClassLoader(); TransformerFactory factory; try { Thread.currentThread().setContextClassLoader(Util.class.getClassLoader()); factory = TransformerFactory.newInstance(); factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, NO_VALUE); factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, NO_VALUE); setFeature(factory::setFeature, XMLConstants.FEATURE_SECURE_PROCESSING, true); } finally { Thread.currentThread().setContextClassLoader(cl); } return factory; } public static SAXParserFactory createSAXParserFactory() { ClassLoader cl = Thread.currentThread().getContextClassLoader(); SAXParserFactory factory; try { Thread.currentThread().setContextClassLoader(Util.class.getClassLoader()); factory = SAXParserFactory.newInstance(); } finally { Thread.currentThread().setContextClassLoader(cl); } return factory; } public static DocumentBuilderFactory createDocumentBuilderFactory() { ClassLoader cl = Thread.currentThread().getContextClassLoader(); DocumentBuilderFactory factory; try { Thread.currentThread().setContextClassLoader(Util.class.getClassLoader()); factory = createLocalDocumentBuilderFactory(); } finally { Thread.currentThread().setContextClassLoader(cl); } return factory; } public static DocumentBuilderFactory createLocalDocumentBuilderFactory() { DocumentBuilderFactory factory; factory = DocumentBuilderFactory.newInstance(); factory.setXIncludeAware(false); factory.setExpandEntityReferences(false); setFeature(factory::setFeature, XMLConstants.FEATURE_SECURE_PROCESSING, true); setPossiblyUnsupportedFeature(factory::setFeature, "http://xml.org/sax/features/external-general-entities", false); setPossiblyUnsupportedFeature(factory::setFeature, "http://xml.org/sax/features/external-parameter-entities", false); setPossiblyUnsupportedFeature(factory::setFeature, "http://apache.org/xml/features/nonvalidating/load-external-dtd", false); return factory; } public static SchemaFactory createSchemaFactory(String uri) { ClassLoader cl = Thread.currentThread().getContextClassLoader(); SchemaFactory factory; try { Thread.currentThread().setContextClassLoader(Util.class.getClassLoader()); factory = SchemaFactory.newInstance(uri); } finally { Thread.currentThread().setContextClassLoader(cl); } return factory; } public static final Map> primitiveTypes = Map.of( "byte" , byte.class , "short" , short.class , "int" , int.class , "long" , long.class , "float" , float.class , "double" , double.class , "boolean" , boolean.class , "char" , char.class ); public static Class loadClass(String name, Object fallbackClass) throws ClassNotFoundException { ClassLoader loader = Util.getCurrentLoader(fallbackClass); return primitiveTypes.getOrDefault(name, Class.forName(name, true, loader)); } public static Class loadClass2(String name, Object fallbackClass) { try { ClassLoader loader = Thread.currentThread().getContextClassLoader(); if (loader == null) { loader = fallbackClass.getClass().getClassLoader(); } return Class.forName(name, true, loader); } catch (ClassNotFoundException e) { throw new IllegalStateException(e.getMessage(), e); } } @SuppressWarnings("unchecked") public static T newInstance(Class clazz) { try { return (T) clazz.getDeclaredConstructor().newInstance(); } catch (IllegalArgumentException | ReflectiveOperationException | SecurityException e) { throw new IllegalStateException(e.getMessage(), e); } } public static ClassLoader getCurrentLoader(Object fallbackClass) { ClassLoader loader = getContextClassLoader(); if (loader == null) { loader = fallbackClass.getClass().getClassLoader(); } return loader; } private static ClassLoader getContextClassLoader() { return Thread.currentThread().getContextClassLoader(); } /** *

* Identify and return the class loader that is associated with the calling web application. *

* * @throws FacesException if the web application class loader cannot be identified */ public static ClassLoader getContextClassLoader2() throws FacesException { // J2EE 1.3 (and later) containers are required to make the // web application class loader visible through the context // class loader of the current thread. ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); if (classLoader == null) { throw new FacesException("getContextClassLoader"); } return classLoader; } public static String removeAllButLastSlashPathSegment(String input) { // Trim the leading lastSlash, if any. if (input.charAt(0) == '/') { input = input.substring(1); } int len = input.length(); // Trim the trailing lastSlash, if any. if (input.charAt(len - 1) == '/') { input = input.substring(0, len - 1); } // Trim any path segments that remain, leaving only the // last path segment. int slash = input.lastIndexOf("/"); // Do we have a "/"? if (-1 != slash) { input = input.substring(slash + 1); } return input; } /** * @return the filename extension or null. the method is null-safe */ public static String fileExtension(String filename) { final String notBlankFilename = nullIfBlank(filename); if ( notBlankFilename == null ) return null; int idx = notBlankFilename.lastIndexOf('.'); return idx == -1 ? null : notBlankFilename.substring(idx+1); } public static String removeAllButNextToLastSlashPathSegment(String input) { // Trim the leading lastSlash, if any. if (input.charAt(0) == '/') { input = input.substring(1); } int len = input.length(); // Trim the trailing lastSlash, if any. if (input.charAt(len - 1) == '/') { input = input.substring(0, len - 1); } // Trim any path segments that remain, leaving only the // last path segment. int lastSlash = input.lastIndexOf("/"); // Do we have a "/"? if (-1 != lastSlash) { int startOrPreviousSlash = input.lastIndexOf("/", lastSlash - 1); startOrPreviousSlash = -1 == startOrPreviousSlash ? 0 : startOrPreviousSlash; input = input.substring(startOrPreviousSlash, lastSlash); } return input; } public static String removeLastPathSegment(String input) { int slash = input.lastIndexOf("/"); // Do we have a "/"? if (-1 != slash) { input = input.substring(0, slash); } return input; } public static void notNegative(String varname, long number) { if (number < 0) { throw new IllegalArgumentException("\"" + varname + "\" is negative"); } } public static void notNull(String varname, Object var) { if (var == null) { throw new NullPointerException(getExceptionMessageString(NULL_PARAMETERS_ERROR_MESSAGE_ID, varname)); } } public static void notNullViewId(String viewId) { if (viewId == null) { throw new IllegalArgumentException(getExceptionMessageString(NULL_VIEW_ID_ERROR_MESSAGE_ID)); } } public static void notNullNamedObject(Object object, String objectId, String logMsg) { if (object == null) { Object[] params = { objectId }; if (LOGGER.isLoggable(SEVERE)) { LOGGER.log(SEVERE, logMsg, params); } throw new FacesException(getExceptionMessageString(NAMED_OBJECT_NOT_FOUND_ERROR_MESSAGE_ID, params)); } } public static void canSetAppArtifact(ApplicationAssociate applicationAssociate, String artifactName) { if (applicationAssociate.hasRequestBeenServiced()) { throw new IllegalStateException(getExceptionMessageString(ILLEGAL_ATTEMPT_SETTING_APPLICATION_ARTIFACT_ID, artifactName)); } } public static void notNullAttribute(String attributeName, Object attribute) { if (attribute == null) { throw new FacesException("The \"" + attributeName + "\" attribute is required"); } } public static ValueExpression getValueExpressionNullSafe(UIComponent component, String name) { ValueExpression valueExpression = component.getValueExpression(name); notNullAttribute(name, valueExpression); return valueExpression; } /** * Returns true if the given string is null or is empty. * * @param string The string to be checked on emptiness. * @return True if the given string is null or is empty. */ public static boolean isEmpty(String string) { return string == null || string.isEmpty(); } /** * Returns true if the given string is null or is blank. * * @param string The string to be checked. * @return True if the given string is null or is blank. */ public static boolean isBlank(String string) { return string == null || string.isBlank(); } /** * @return null if the passed String is null or blank, s otherwise */ public static String nullIfBlank(String s) { return isBlank(s) ? null : s; } /** * Returns true if the given array is null or is empty. * * @param array The array to be checked on emptiness. * @return true if the given array is null or is empty. */ public static boolean isEmpty(Object[] array) { return array == null || array.length == 0; } /** * Returns true if the given collection is null or is empty. * * @param collection The collection to be checked on emptiness. * @return true if the given collection is null or is empty. */ public static boolean isEmpty(Collection collection) { return collection == null || collection.isEmpty(); } /** * Returns true if the given value is null or is empty. Types of String, Collection, Map, Optional and * Array are recognized. If none is recognized, then examine the emptiness of the toString() representation instead. * * @param value The value to be checked on emptiness. * @return true if the given value is null or is empty. */ public static boolean isEmpty(Object value) { if (value == null) { return true; } else if (value instanceof String) { return ((String) value).isEmpty(); } else if (value instanceof Collection) { return ((Collection) value).isEmpty(); } else if (value instanceof Map) { return ((Map) value).isEmpty(); } else if (value instanceof Optional) { return ((Optional) value).isEmpty(); } else if (value.getClass().isArray()) { return Array.getLength(value) == 0; } else { return value.toString() == null || value.toString().isEmpty(); } } /** * Returns true if all values are empty, false if at least one value is not empty. * * @param values the values to be checked on emptiness * @return True if all values are empty, false otherwise */ public static boolean isAllEmpty(Object... values) { for (Object value : values) { if (!isEmpty(value)) { return false; } } return true; } /** * Returns true if at least one value is empty. * * @param values the values to be checked on emptiness * @return true if any value is empty and false if no values are empty */ public static boolean isAnyEmpty(Object... values) { for (Object value : values) { if (isEmpty(value)) { return true; } } return false; } public static boolean isAllNull(Object... values) { for (Object value : values) { if (value != null) { return false; } } return true; } public static boolean isAnyNull(Object... values) { for (Object value : values) { if (value == null) { return true; } } return false; } /** * Returns true if the given object equals one of the given objects. * * @param The generic object type. * @param object The object to be checked if it equals one of the given objects. * @param objects The argument list of objects to be tested for equality. * @return true if the given object equals one of the given objects. */ @SafeVarargs public static boolean isOneOf(T object, T... objects) { for (Object other : objects) { if (object == null ? other == null : object.equals(other)) { return true; } } return false; } /** * Returns the first non-null object of the argument list, or null if there is no such * element. * * @param The generic object type. * @param objects The argument list of objects to be tested for non-null. * @return The first non-null object of the argument list, or null if there is no such * element. */ @SafeVarargs public static T coalesce(T... objects) { for (T object : objects) { if (object != null) { return object; } } return null; } public static List reverse(List list) { int length = list.size(); List result = new ArrayList<>(length); for (int i = length - 1; i >= 0; i--) { result.add(list.get(i)); } return result; } /** * Returns true if the given string starts with one of the given prefixes. * * @param string The object to be checked if it starts with one of the given prefixes. * @param prefixes The argument list of prefixes to be checked * * @return true if the given string starts with one of the given prefixes. */ public static boolean startsWithOneOf(String string, String... prefixes) { if (prefixes == null) { return false; } for (String prefix : prefixes) { if (string.startsWith(prefix)) { return true; } } return false; } /** * @param context the FacesContext for the current request * @return the Locale from the UIViewRoot, the the value of Locale.getDefault() */ public static Locale getLocaleFromContextOrSystem(FacesContext context) { Locale result, temp = Locale.getDefault(); UIViewRoot root; result = temp; if (null != context && null != (root = context.getViewRoot()) && null == (result = root.getLocale())) { result = temp; } return result; } public static Converter getConverterForClass(Class converterClass, FacesContext context) { if (converterClass == null) { return null; } try { Application application = context.getApplication(); return application.createConverter(converterClass); } catch (Exception e) { return null; } } public static Converter getConverterForIdentifer(String converterId, FacesContext context) { if (converterId == null) { return null; } try { Application application = context.getApplication(); return application.createConverter(converterId); } catch (Exception e) { return null; } } public static StateManager getStateManager(FacesContext context) throws FacesException { return context.getApplication().getStateManager(); } public static Class getTypeFromString(String type) throws ClassNotFoundException { Class result; switch (type) { case "byte": result = Byte.TYPE; break; case "short": result = Short.TYPE; break; case "int": result = Integer.TYPE; break; case "long": result = Long.TYPE; break; case "float": result = Float.TYPE; break; case "double": result = Double.TYPE; break; case "boolean": result = Boolean.TYPE; break; case "char": result = Character.TYPE; break; case "void": result = Void.TYPE; break; default: if (type.indexOf('.') == -1) { type = "java.lang." + type; } result = Util.loadClass(type, Void.TYPE); break; } return result; } public static ViewHandler getViewHandler(FacesContext context) throws FacesException { // Get Application instance Application application = context.getApplication(); assert application != null; // Get the ViewHandler ViewHandler viewHandler = application.getViewHandler(); assert viewHandler != null; return viewHandler; } public static boolean componentIsDisabled(UIComponent component) { return Boolean.parseBoolean(String.valueOf(component.getAttributes().get("disabled"))); } public static boolean componentIsDisabledOrReadonly(UIComponent component) { return Boolean.parseBoolean(String.valueOf(component.getAttributes().get("disabled"))) || Boolean.parseBoolean(String.valueOf(component.getAttributes().get("readonly"))); } // 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; try { Method method = Locale.class.getMethod("forLanguageTag", String.class); if (method != null) { result = (Locale) method.invoke(null, localeStr); } } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException throwable) { // if we are NOT running JavaSE 7 we end up here and we will // default to the previous way of determining the Locale below. } if (result == null || result.getLanguage().equals("")) { String lang = null; String country = null; String variant = null; char[] seps = { '-', '_' }; int inputLength = localeStr.length(); int i = 0; int j = 0; // to have a language, the length must be >= 2 if (inputLength >= 2 && (i = indexOfSet(localeStr, seps, 0)) == -1) { // 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 (i != -1) { lang = localeStr.substring(0, i); // look for the country sep. // to have a country, the length must be >= 5 if (inputLength >= 5 && (j = indexOfSet(localeStr, seps, i + 1)) == -1) { // no further separators, length must be 5 if (inputLength != 5) { throw new IllegalArgumentException("Illegal locale String: " + localeStr); } country = localeStr.substring(i + 1); } if (j != -1) { country = localeStr.substring(i + 1, j); // if we have enough separators for language, locale, // and variant, the length must be >= 8. if (inputLength >= 8) { variant = localeStr.substring(j + 1); } else { throw new IllegalArgumentException("Illegal locale String: " + localeStr); } } } if (variant != null && country != null && lang != null) { result = new Locale(lang, country, variant); } else if (lang != null && country != null) { result = new Locale(lang, country); } else if (lang != null) { result = new Locale(lang, ""); } } return result; } /** * @param str local string * @param set the substring * @param fromIndex starting index * @return starting at fromIndex, the index of the first occurrence of any substring from set * in toSearch, or -1 if no such match is found */ public static int indexOfSet(String str, char[] set, int fromIndex) { int result = -1; for (int i = fromIndex, len = str.length(); i < len; i++) { for (int j = 0, innerLen = set.length; j < innerLen; j++) { if (str.charAt(i) == set[j]) { result = i; break; } } if (-1 != result) { break; } } return result; } /** *

* Leverage the Throwable.getStackTrace() method to produce a String version of the stack trace, with a "\n" before each * line. *

* * @param e the Throwable to obtain the stacktrace from * * @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 ""; } StackTraceElement[] stacks = e.getStackTrace(); StringBuilder sb = new StringBuilder(); for (StackTraceElement stack : stacks) { sb.append(stack.toString()).append('\n'); } return sb.toString(); } /** *

* PRECONDITION: argument response is non-null and has a method called getContentType that * takes no arguments and returns a String, with no side-effects. *

* *

* This method allows us to get the contentType in both the servlet and portlet cases, without introducing a * compile-time dependency on the portlet api. *

* * @param response the current response * @return the content type of the response */ public static String getContentTypeFromResponse(Object response) { String result = null; if (null != response) { try { Method method = ReflectionUtils.lookupMethod(response.getClass(), "getContentType", RIConstants.EMPTY_CLASS_ARGS); if (null != method) { Object obj = method.invoke(response, RIConstants.EMPTY_METH_ARGS); if (null != obj) { result = obj.toString(); } } } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { throw new FacesException(e); } } return result; } /** *

* A slightly more efficient version of String.split() which caches the Patterns in an LRUMap * instead of creating a new Pattern on each invocation. *

* * @param appMap the Application Map * @param toSplit the string to split * @param regex the regex used for splitting * @return the result of Pattern.spit(String, int) */ public static String[] split(Map appMap, String toSplit, String regex) { return split(appMap, toSplit, regex, 0); } /** *

A slightly more efficient version of * String.split() which caches * the Patterns in an LRUMap instead of * creating a new Pattern on each * invocation. Limited by splitLimit.

* @param appMap the Application Map * @param toSplit the string to split * @param regex the regex used for splitting * @param splitLimit split result threshold * @return the result of Pattern.spit(String, int) */ public static String[] split(Map appMap, String toSplit, String regex, int splitLimit) { Map patternCache = getPatternCache(appMap); Pattern pattern = patternCache.computeIfAbsent(regex, Pattern::compile); return pattern.split(toSplit, splitLimit); } public static String[] split(ServletContext sc, String toSplit, String regex) { Map patternCache = getPatternCache(sc); Pattern pattern = patternCache.computeIfAbsent(regex, Pattern::compile); return pattern.split(toSplit, 0); } /** *

* Returns the URL pattern of the {@link jakarta.faces.webapp.FacesServlet} that is executing the current request. If * there are multiple URL patterns, the value returned by HttpServletRequest.getServletPath() and * HttpServletRequest.getPathInfo() is used to determine which mapping to return. *

* If no mapping can be determined, it most likely means that this particular request wasn't dispatched through the * {@link jakarta.faces.webapp.FacesServlet}. *

* * @param context the {@link FacesContext} of the current request * * @return the URL pattern of the {@link jakarta.faces.webapp.FacesServlet} or null if no mapping can be * determined * * @throws NullPointerException if context is null */ public static HttpServletMapping getFacesMapping(FacesContext context) { notNull("context", context); return ((HttpServletRequest) context.getExternalContext().getRequest()).getHttpServletMapping(); } /** * Checks if the FacesServlet is exact mapped to the given resource. *

* Not to be confused with isExactMapped(String), which checks if a string representing a mapping, not a * resource, is an exact mapping. * * @param viewId the view id to test * @return true if the FacesServlet is exact mapped to the given viewId, false otherwise */ public static boolean isViewIdExactMappedToFacesServlet(String viewId) { return isResourceExactMappedToFacesServlet(FacesContext.getCurrentInstance().getExternalContext(), viewId); } /** * Checks if the FacesServlet is exact mapped to the given resource. *

* Not to be confused with isExactMapped(String), which checks if a string representing a mapping, not a * resource, is an exact mapping. * * @param externalContext the external context for this request * @param resource the resource to test * @return true if the FacesServlet is exact mapped to the given resource, false otherwise */ public static boolean isResourceExactMappedToFacesServlet(ExternalContext externalContext, String resource) { Object context = externalContext.getContext(); if (context instanceof ServletContext) { return getFacesServletMappings((ServletContext) context).contains(resource); } return false; } public static HttpServletMapping getFirstWildCardMappingToFacesServlet(ExternalContext externalContext) { // If needed, cache this after initialization of Faces Object context = externalContext.getContext(); if (context instanceof ServletContext) { return getFacesServletMappings((ServletContext) context).stream() .filter(mapping -> mapping.contains("*")) .map(mapping -> new HttpServletMapping() { @Override public String getServletName() { return ""; } @Override public String getPattern() { return mapping; } @Override public String getMatchValue() { return null; } @Override public MappingMatch getMappingMatch() { return isPrefixMapped(mapping)? MappingMatch.PATH : MappingMatch.EXTENSION; } }) .findFirst() .orElse(null); } return null; } /** *

* Returns true if the provided url-mapping is a prefix path mapping (starts with /). *

* * @param mapping a url-pattern * @return true if the mapping starts with / */ public static boolean isPrefixMapped(String mapping) { return mapping.charAt(0) == '/'; } public static boolean isSpecialAttributeName(String name) { boolean isSpecialAttributeName = name.equals("action") || name.equals("actionListener") || name.equals("validator") || name.equals("valueChangeListener"); return isSpecialAttributeName; } /** * @param ctx the {@link FacesContext} for the current request * @param viewToRender the {@link UIViewRoot} to check * @return true if the {@link FacesContext} attributes map contains a reference to the {@link UIViewRoot}'s * view ID */ public static boolean isViewPopulated(FacesContext ctx, UIViewRoot viewToRender) { return ctx.getAttributes().containsKey(viewToRender); } /** *

* Flag the specified {@link UIViewRoot} as populated. *

* * @param ctx the {@link FacesContext} for the current request * @param viewToRender the {@link UIViewRoot} to mark as populated */ public static void setViewPopulated(FacesContext ctx, UIViewRoot viewToRender) { ctx.getAttributes().put(viewToRender, Boolean.TRUE); } /** * Utility method to validate ID uniqueness for the tree represented by component. */ public static void checkIdUniqueness(FacesContext context, UIComponent component, Set componentIds) { boolean uniquenessCheckDisabled = false; if (context.isProjectStage(ProjectStage.Production)) { WebConfiguration config = WebConfiguration.getInstance(context.getExternalContext()); uniquenessCheckDisabled = config.isOptionEnabled(WebConfiguration.BooleanWebContextInitParameter.DisableIdUniquenessCheck); } if (!uniquenessCheckDisabled) { // deal with children/facets that are marked transient. for (Iterator kids = component.getFacetsAndChildren(); kids.hasNext();) { UIComponent kid = kids.next(); // check for id uniqueness String id = kid.getClientId(context); if (componentIds.add(id)) { checkIdUniqueness(context, kid, componentIds); } else { if (LOGGER.isLoggable(Level.SEVERE)) { LOGGER.log(Level.SEVERE, "faces.duplicate_component_id_error", id); FastStringWriter writer = new FastStringWriter(128); DebugUtil.simplePrintTree(context.getViewRoot(), id, writer); LOGGER.severe(writer.toString()); } String message = MessageUtils.getExceptionMessageString(MessageUtils.DUPLICATE_COMPONENT_ID_ERROR_ID, id); throw new IllegalStateException(message); } } } } static public boolean classHasAnnotations(Class clazz) { if (clazz != null) { while (clazz != Object.class) { try { Field[] fields = clazz.getDeclaredFields(); if (fields != null) { for (Field field : fields) { if (field.getAnnotations().length > 0) { return true; } } } Method[] methods = clazz.getDeclaredMethods(); if (methods != null) { for (Method method : methods) { if (method.getDeclaredAnnotations().length > 0) { return true; } } } } catch (NoClassDefFoundError e) { if (LOGGER.isLoggable(FINE)) { LOGGER.log(FINE, "Cannot inspect " + clazz + " because of missing dependency " + e.getMessage()); } return false; } clazz = clazz.getSuperclass(); } } return false; } /** * If view root is instance of naming container, return its container client id, suffixed with separator character. * * @param context Involved faces context. * @return The naming container prefix, or an empty string if the view root is not an instance of naming container. */ public static String getNamingContainerPrefix(FacesContext context) { UIViewRoot viewRoot = context.getViewRoot(); if (viewRoot == null) { Application application = context.getApplication(); viewRoot = (UIViewRoot) application.createComponent(UIViewRoot.COMPONENT_TYPE); } if (viewRoot instanceof NamingContainer) { return viewRoot.getContainerClientId(context) + UINamingContainer.getSeparatorChar(context); } else { return ""; } } public static String getViewStateId(FacesContext context) { String result = null; final String viewStateCounterKey = "com.sun.faces.util.ViewStateCounterKey"; Map contextAttrs = context.getAttributes(); Integer counter = (Integer) contextAttrs.get(viewStateCounterKey); if (null == counter) { counter = Integer.valueOf(0); } char sep = UINamingContainer.getSeparatorChar(context); UIViewRoot root = context.getViewRoot(); result = root.getContainerClientId(context) + sep + ResponseStateManager.VIEW_STATE_PARAM + sep + +counter; contextAttrs.put(viewStateCounterKey, ++counter); return result; } public static String getClientWindowId(FacesContext context) { String result = null; final String clientWindowIdCounterKey = "com.sun.faces.util.ClientWindowCounterKey"; Map contextAttrs = context.getAttributes(); Integer counter = (Integer) contextAttrs.get(clientWindowIdCounterKey); if (null == counter) { counter = Integer.valueOf(0); } char sep = UINamingContainer.getSeparatorChar(context); result = context.getViewRoot().getContainerClientId(context) + sep + ResponseStateManager.CLIENT_WINDOW_PARAM + sep + counter; contextAttrs.put(clientWindowIdCounterKey, ++counter); return result; } private static final String FACES_CONTEXT_ATTRIBUTES_DOCTYPE_KEY = Util.class.getName() + "_FACES_CONTEXT_ATTRS_DOCTYPE_KEY"; public static void saveDOCTYPEToFacesContextAttributes(Doctype doctype) { FacesContext context = FacesContext.getCurrentInstance(); if (null == context) { return; } Map attrs = context.getAttributes(); attrs.put(FACES_CONTEXT_ATTRIBUTES_DOCTYPE_KEY, doctype); } public static Doctype getDOCTYPEFromFacesContextAttributes(FacesContext context) { if (null == context) { return null; } Map attrs = context.getAttributes(); return (Doctype) attrs.get(FACES_CONTEXT_ATTRIBUTES_DOCTYPE_KEY); } private static final String FACES_CONTEXT_ATTRIBUTES_XMLDECL_KEY = Util.class.getName() + "_FACES_CONTEXT_ATTRS_XMLDECL_KEY"; public static void saveXMLDECLToFacesContextAttributes(String XMLDECL) { FacesContext context = FacesContext.getCurrentInstance(); if (null == context) { return; } Map attrs = context.getAttributes(); attrs.put(FACES_CONTEXT_ATTRIBUTES_XMLDECL_KEY, XMLDECL); } public static String getXMLDECLFromFacesContextAttributes(FacesContext context) { if (null == context) { return null; } Map attrs = context.getAttributes(); return (String) attrs.get(FACES_CONTEXT_ATTRIBUTES_XMLDECL_KEY); } public static long getLastModified(URL url) { long lastModified; URLConnection conn; InputStream is = null; try { conn = url.openConnection(); if (conn instanceof JarURLConnection) { /* * Note this is a work around for JarURLConnection since the getLastModified method is buggy. See JAVASERVERFACES-2725 * and JAVASERVERFACES-2734. */ JarURLConnection jarUrlConnection = (JarURLConnection) conn; URL jarFileUrl = jarUrlConnection.getJarFileURL(); URLConnection jarFileConnection = jarFileUrl.openConnection(); lastModified = jarFileConnection.getLastModified(); jarFileConnection.getInputStream().close(); } else { is = conn.getInputStream(); lastModified = conn.getLastModified(); } } catch (Exception e) { throw new FacesException("Error Checking Last Modified for " + url, e); } finally { if (is != null) { try { is.close(); } catch (Exception e) { if (LOGGER.isLoggable(Level.FINEST)) { LOGGER.log(Level.FINEST, "Closing stream", e); } } } } return lastModified; } /** * Get the faces-config.xml version (if any). * * @param facesContext the Faces context. * @return the version found, or "" if none found. */ public static String getFacesConfigXmlVersion(FacesContext facesContext) { String result = ""; InputStream stream = null; try { URL url = facesContext.getExternalContext().getResource("/WEB-INF/faces-config.xml"); if (url != null) { XPathFactory factory = XPathFactory.newInstance(); XPath xpath = factory.newXPath(); xpath.setNamespaceContext(new JakartaNamespaceContext()); stream = url.openStream(); DocumentBuilderFactory dbf = createDocumentBuilderFactory(); try { dbf.setFeature("http://xml.org/sax/features/external-general-entities", false); dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false); dbf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false); } catch (ParserConfigurationException pce) { } dbf.setNamespaceAware(true); dbf.setValidating(false); dbf.setXIncludeAware(false); dbf.setExpandEntityReferences(false); result = xpath.evaluate("string(/" + JakartaNamespaceContext.PREFIX + ":faces-config/@version)", dbf.newDocumentBuilder().parse(stream)); } } catch (MalformedURLException mue) { } catch (XPathExpressionException | IOException xpee) { } catch (Exception e) { } finally { if (stream != null) { try { stream.close(); } catch (IOException ioe) { } } } return result; } /** * Get the web.xml version (if any). * * @param facesContext the Faces context. * @return the version found, or "" if none found. */ public static String getWebXmlVersion(FacesContext facesContext) { String result = ""; InputStream stream = null; try { URL url = facesContext.getExternalContext().getResource("/WEB-INF/web.xml"); if (url != null) { XPathFactory factory = XPathFactory.newInstance(); XPath xpath = factory.newXPath(); xpath.setNamespaceContext(new JakartaNamespaceContext()); stream = url.openStream(); DocumentBuilderFactory dbf = createDocumentBuilderFactory(); try { dbf.setFeature("http://xml.org/sax/features/external-general-entities", false); dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false); dbf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false); } catch (ParserConfigurationException e) { } dbf.setNamespaceAware(true); dbf.setValidating(false); dbf.setXIncludeAware(false); dbf.setExpandEntityReferences(false); result = xpath.evaluate("string(/" + JakartaNamespaceContext.PREFIX + ":web-app/@version)", dbf.newDocumentBuilder().parse(stream)); } } catch (MalformedURLException mue) { } catch (XPathExpressionException | IOException xpee) { } catch (Exception e) { } finally { if (stream != null) { try { stream.close(); } catch (IOException ioe) { } } } return result; } public static class JakartaNamespaceContext implements NamespaceContext { public static final String PREFIX = "jakartaee"; @Override public String getNamespaceURI(String prefix) { return FacesSchema.Schemas.JAKARTAEE_SCHEMA_DEFAULT_NS; } @Override public String getPrefix(String namespaceURI) { return PREFIX; } @Override public Iterator getPrefixes(String namespaceURI) { return null; } } /** * Get the CDI bean manager. * * @param facesContext the Faces context to consult * @return the CDI bean manager. */ public static ELAwareBeanManager getCdiBeanManager(FacesContext facesContext) { ELAwareBeanManager result = null; if (facesContext != null && facesContext.getAttributes().containsKey(CDI_BEAN_MANAGER)) { result = (ELAwareBeanManager) facesContext.getAttributes().get(CDI_BEAN_MANAGER); } else if (facesContext != null && facesContext.getExternalContext().getApplicationMap().containsKey(CDI_BEAN_MANAGER)) { result = (ELAwareBeanManager) facesContext.getExternalContext().getApplicationMap().get(CDI_BEAN_MANAGER); } else { try { result = wrapIfNeeded(InitialContext.doLookup("java:comp/BeanManager")); } catch (NamingException ne) { try { result = wrapIfNeeded(InitialContext.doLookup("java:comp/env/BeanManager")); } catch (NamingException ne2) { try { result = wrapIfNeeded(CDI.current().getBeanManager()); } catch (Exception | LinkageError e) { } } } if (result == null && facesContext != null) { Map applicationMap = facesContext.getExternalContext().getApplicationMap(); result = wrapIfNeeded((BeanManager) applicationMap.get("org.jboss.weld.environment.servlet.jakarta.enterprise.inject.spi.BeanManager")); } if (result != null && facesContext != null) { facesContext.getAttributes().put(CDI_BEAN_MANAGER, result); facesContext.getExternalContext().getApplicationMap().put(CDI_BEAN_MANAGER, result); } } if (result == null) { throw new IllegalStateException("CDI is not available"); } return result; } private static ELAwareBeanManager wrapIfNeeded(BeanManager beanManager) { if (beanManager == null) { return null; } if (beanManager instanceof ELAwareBeanManager elAwareBeanManager) { return elAwareBeanManager; } return new ELAwareBeanManagerWrapper(beanManager); } @SuppressWarnings("unchecked") public static Stream stream(Object object) { if (object == null) { return Stream.empty(); } else if (object instanceof Stream) { return (Stream) object; } else if (object instanceof Collection) { return ((Collection)object).stream(); // little bonus with sized spliterator... } else if ( object instanceof Enumeration ) { // recursive call wrapping in an Iterator (Java 9+) return stream( ((Enumeration)object).asIterator() ); } else if (object instanceof Iterable) { return (Stream) StreamSupport.stream(((Iterable) object).spliterator(), false); } else if (object instanceof Map) { return (Stream) ((Map) object).entrySet().stream(); } else if (object instanceof int[]) { return (Stream) Arrays.stream((int[]) object).boxed(); } else if (object instanceof long[]) { return (Stream) Arrays.stream((long[]) object).boxed(); } else if (object instanceof double[]) { return (Stream) Arrays.stream((double[]) object).boxed(); } else if (object instanceof Object[]) { return (Stream) Arrays.stream((Object[]) object); } else { return (Stream) Stream.of(object); } } @SafeVarargs public static Set unmodifiableSet(E... elements) { return Collections.unmodifiableSet(new LinkedHashSet<>(Arrays.asList(elements))); } public static boolean isNestedInIterator(FacesContext context, UIComponent component) { UIComponent parent = component.getParent(); if (parent == null) { return false; } for (UIComponent p = parent; p != null; p = p.getParent()) { if (p instanceof UIData || p instanceof UIRepeat) { return true; } } // https://github.com/eclipse-ee4j/mojarra/issues/4957 // We should in long term probably introduce a common interface like UIIterable. // But this is solid for now as all known implementing components already follow this pattern. // We could theoretically even remove the above instanceof checks. Pattern clientIdNestedInIteratorPattern = getPatternCache(context.getExternalContext().getApplicationMap()).computeIfAbsent(CLIENT_ID_NESTED_IN_ITERATOR_PATTERN, k -> { String separatorChar = Pattern.quote(String.valueOf(UINamingContainer.getSeparatorChar(context))); return Pattern.compile(".+" + separatorChar + "[0-9]+" + separatorChar + ".+"); }); return clientIdNestedInIteratorPattern.matcher(parent.getClientId(context)).matches(); } public static String ensureLeadingSlash(String s) { if (s == null || (!s.isEmpty() && s.charAt(0) == '/')) { return s; } else { return '/' + s; } } /** * Extract first numeric segment from given client ID. *
    *
  • 'table:1:button' should return 1
  • *
  • 'table:2' should return 2
  • *
  • '3:button' should return 3
  • *
  • '4' should return 4
  • *
* @param clientId the client ID * @param separatorChar the separator character * @return first numeric segment from given client ID. * @throws NumberFormatException when given client ID doesn't have any numeric segment at all. */ public static int extractFirstNumericSegment(String clientId, char separatorChar) { int nextSeparatorChar = clientId.indexOf(separatorChar); while (clientId.length() > 0 && !isDigit(clientId.charAt(0)) && nextSeparatorChar >= 0) { clientId = clientId.substring(nextSeparatorChar + 1); nextSeparatorChar = clientId.indexOf(separatorChar); } if (clientId.length() > 0 && isDigit(clientId.charAt(0))) { String firstNumericSegment = nextSeparatorChar >= 0 ? clientId.substring(0, nextSeparatorChar) : clientId; return Integer.parseInt(firstNumericSegment); } throw new NumberFormatException("there is no numeric segment"); } /** * @param context the {@link FacesContext} for the current request * @return the encoding to be used for the response */ public static String getResponseEncoding(FacesContext context) { return getResponseEncoding(context, Optional.empty()); } /** * @param context the {@link FacesContext} for the current request * @param defaultEncoding the default encoding, if any * @return the encoding to be used for the response */ public static String getResponseEncoding(FacesContext context, Optional defaultEncoding) { // 1. First get it from viewroot, if any. if (context.getViewRoot() != null) { String encoding = (String) context.getViewRoot().getAttributes().get(FACELETS_ENCODING_KEY); if (encoding != null) { // If found, then immediately return it, this represents either the encoding explicitly set via or the one actually set on response. // See also ViewHandler#apply() and FaceletViewHandlingStrategy#createResponseWriter(). return encoding; } } // 2. If none found then get it from context (this is usually set during compile/buildtime based on request character encoding). // See also SAXCompiler#doCompile() and EncodingHandler#apply(). String encoding = (String) context.getAttributes().get(FACELETS_ENCODING_KEY); if (encoding != null && LOGGER.isLoggable(FINEST)) { LOGGER.log(FINEST, "Using Facelet encoding {0}", encoding); } if (encoding == null) { // 3. If none found then get it from request (could happen when the view isn't built yet). // See also ViewHandler#initView() and ViewHandler#calculateCharacterEncoding(). encoding = context.getExternalContext().getRequestCharacterEncoding(); if (encoding != null && LOGGER.isLoggable(FINEST)) { LOGGER.log(FINEST, "Using Request encoding {0}", encoding); } } if (encoding == null && context.getExternalContext().getSession(false) != null) { // 4. If still none found then get previously known request encoding from session. // See also ViewHandler#initView(). encoding = (String) context.getExternalContext().getSessionMap().get(CHARACTER_ENCODING_KEY); if (encoding != null && LOGGER.isLoggable(FINEST)) { LOGGER.log(FINEST, "Using Session encoding {0}", encoding); } } if (encoding == null) { // 5. If still none found then fall back to specified default. if (defaultEncoding.isPresent()) { encoding = defaultEncoding.get(); } if (encoding != null && !encoding.isBlank()) { if (LOGGER.isLoggable(FINEST)) { LOGGER.log(FINEST, "Using specified default encoding {0}", encoding); } } else { // 6. If specified default is null or blank then finally fall back to hardcoded default. encoding = RIConstants.CHAR_ENCODING; if (LOGGER.isLoggable(FINEST)) { LOGGER.log(FINEST, "No encoding found, defaulting to {0}", encoding); } } } return encoding; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy