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

org.primefaces.expression.SearchExpressionFacade Maven / Gradle / Ivy

Go to download

PrimeFaces is one of the most popular UI libraries in Java EE Ecosystem and widely used by software companies, world renowned brands, banks, financial institutions, insurance companies, universities and more.

There is a newer version: 14.0.0
Show newest version
/**
 * Copyright 2009-2018 PrimeTek.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.primefaces.expression;

import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;

import javax.faces.FacesException;
import javax.faces.application.ProjectStage;
import javax.faces.component.ContextCallback;
import javax.faces.component.UIComponent;
import javax.faces.component.UINamingContainer;
import javax.faces.context.FacesContext;
import org.primefaces.util.ComponentTraversalUtils;

import org.primefaces.util.ComponentUtils;
import org.primefaces.util.SharedStringBuilder;

/**
 * Simple facade for the whole Search Expression module.
 */
public class SearchExpressionFacade {

    private static final Logger LOG = Logger.getLogger(SearchExpressionFacade.class.getName());

    private static final String SHARED_EXPRESSION_BUFFER_KEY = SearchExpressionFacade.class.getName() + ".SHARED_EXPRESSION_BUFFER";
    private static final String SHARED_SPLIT_BUFFER_KEY = SearchExpressionFacade.class.getName() + ".SHARED_SPLIT_BUFFER_KEY";
    private static final String SHARED_CLIENT_ID_EXPRESSION_BUFFER_KEY = SearchExpressionFacade.class.getName() + ".SHARED_CLIENT_ID_EXPRESSION_BUFFER_KEY";

    private static final char[] EXPRESSION_SEPARATORS = new char[] { ',', ' ' };

    /**
     * Resolves a list of {@link UIComponent}s for the given expression or expressions.
     *
     * @param context The {@link FacesContext}.
     * @param source The source component. E.g. a button.
     * @param expressions The search expressions.
     * @return A {@link List} with resolved {@link UIComponent}s.
     */
    public static List resolveComponents(FacesContext context, UIComponent source, String expressions) {
        return resolveComponents(context, source, expressions, SearchExpressionHint.NONE);
    }

    /**
     * Resolves a list of {@link UIComponent}s for the given expression or expressions.
     *
     * @param context The {@link FacesContext}.
     * @param source The source component. E.g. a button.
     * @param expressions The search expressions.
     * @param hints The hints.
     * @return A {@link List} with resolved {@link UIComponent}s.
     */
    public static List resolveComponents(FacesContext context, UIComponent source, String expressions, int hints) {

        ArrayList components = new ArrayList();

        if (!ComponentUtils.isValueBlank(expressions)) {
            String[] splittedExpressions = splitExpressions(context, source, expressions);

            if (splittedExpressions != null && splittedExpressions.length > 0) {

                final char separatorChar = UINamingContainer.getSeparatorChar(context);
                final String separatorString = String.valueOf(separatorChar);

                for (String splittedExpression : splittedExpressions) {
                    String expression = splittedExpression.trim();

                    if (ComponentUtils.isValueBlank(expression)) {
                        continue;
                    }

                    // if it contains a keyword and it's not a nested expression (e.g. @parent:@parent), we don't need to loop
                    if (expression.contains(SearchExpressionConstants.KEYWORD_PREFIX) && expression.contains(separatorString)) {
                        components.addAll(resolveComponentsByExpressionChain(context, source, expression, separatorChar, separatorString, hints));
                    }
                    else {
                        // it's a keyword and not nested, just ask our resolvers
                        if (expression.contains(SearchExpressionConstants.KEYWORD_PREFIX)) {
                            SearchExpressionResolver resolver = SearchExpressionResolverFactory.findResolver(expression);

                            if (resolver instanceof MultiSearchExpressionResolver) {
                                ((MultiSearchExpressionResolver) resolver).resolveComponents(context, source, source, expression, components, hints);
                            }
                            else {
                                UIComponent component = resolver.resolveComponent(context, source, source, expression, hints);

                                if (component == null) {
                                    if (!SearchExpressionUtils.isHintSet(hints, SearchExpressionHint.IGNORE_NO_RESULT)) {
                                        cannotFindComponent(context, source, expression);
                                    }
                                }
                                else {
                                    components.add(component);
                                }
                            }
                        } // default ID case
                        else {
                            ResolveComponentCallback callback = new ResolveComponentCallback();
                            resolveComponentById(source, expression, separatorString, context, callback);
                            UIComponent component = callback.getComponent();

                            if (component == null) {
                                if (!SearchExpressionUtils.isHintSet(hints, SearchExpressionHint.IGNORE_NO_RESULT)) {
                                    cannotFindComponent(context, source, expression);
                                }
                            }
                            else {
                                components.add(component);
                            }
                        }
                    }
                }
            }
        }

        return components;
    }

    /**
     * Resolves a list of {@link UIComponent} clientIds and/or passtrough expressions for the given expression or expressions.
     *
     * @param context The {@link FacesContext}.
     * @param source The source component. E.g. a button.
     * @param expressions The search expressions.
     * @return A {@link List} with resolved clientIds and/or passtrough expression (like PFS, widgetVar).
     */
    public static String resolveClientIds(FacesContext context, UIComponent source, String expressions) {

        return resolveClientIds(context, source, expressions, SearchExpressionHint.NONE);
    }

    /**
     * Resolves a list of {@link UIComponent} clientIds and/or passtrough expressions for the given expression or expressions.
     *
     * @param context The {@link FacesContext}.
     * @param source The source component. E.g. a button.
     * @param expressions The search expressions.
     * @param hints The hints.
     * @return A {@link List} with resolved clientIds and/or passtrough expression (like PFS, widgetVar).
     */
    public static String resolveClientIds(FacesContext context, UIComponent source, String expressions, int hints) {

        if (ComponentUtils.isValueBlank(expressions)) {
            if (SearchExpressionUtils.isHintSet(hints, SearchExpressionHint.PARENT_FALLBACK)) {
                return source.getParent().getClientId(context);
            }

            return null;
        }

        String[] splittedExpressions = splitExpressions(context, source, expressions);

        if (splittedExpressions != null && splittedExpressions.length > 0) {

            final char separatorChar = UINamingContainer.getSeparatorChar(context);
            final String separatorString = String.valueOf(separatorChar);

            StringBuilder expressionsBuffer = SharedStringBuilder.get(context, SHARED_EXPRESSION_BUFFER_KEY);

            for (int i = 0; i < splittedExpressions.length; i++) {
                String expression = splittedExpressions[i].trim();

                if (ComponentUtils.isValueBlank(expression)) {
                    continue;
                }

                validateExpression(context, source, expression, separatorChar, separatorString);

                if (isPassTroughExpression(expression)) {
                    if (expressionsBuffer.length() > 0) {
                        expressionsBuffer.append(" ");
                    }
                    expressionsBuffer.append(expression);
                }
                else {
                    // if it contains a keyword and it's not a nested expression (e.g. @parent:@parent), we don't need to loop
                    if (expression.contains(SearchExpressionConstants.KEYWORD_PREFIX) && expression.contains(separatorString)) {
                        String clientIds = resolveClientIdsByExpressionChain(context, source, expression, separatorChar, separatorString, hints);
                        if (!ComponentUtils.isValueBlank(clientIds)) {
                            if (expressionsBuffer.length() > 0) {
                                expressionsBuffer.append(" ");
                            }
                            expressionsBuffer.append(clientIds);
                        }
                    }
                    else {
                        // it's a keyword and not nested, just ask our resolvers
                        if (expression.contains(SearchExpressionConstants.KEYWORD_PREFIX)) {
                            SearchExpressionResolver resolver = SearchExpressionResolverFactory.findResolver(expression);

                            if (resolver instanceof ClientIdSearchExpressionResolver) {
                                String clientIds = ((ClientIdSearchExpressionResolver) resolver).resolveClientIds(context, source, source, expression, hints);
                                if (!ComponentUtils.isValueBlank(clientIds)) {
                                    if (expressionsBuffer.length() > 0) {
                                        expressionsBuffer.append(" ");
                                    }
                                    expressionsBuffer.append(clientIds);
                                }
                            }
                            else if (resolver instanceof MultiSearchExpressionResolver) {
                                ArrayList result = new ArrayList();
                                ((MultiSearchExpressionResolver) resolver).resolveComponents(context, source, source, expression, result, hints);
                                for (int j = 0; j < result.size(); j++) {
                                    UIComponent component = result.get(j);
                                    validateRenderer(context, source, component, expression, hints);
                                    if (expressionsBuffer.length() > 0) {
                                        expressionsBuffer.append(" ");
                                    }
                                    expressionsBuffer.append(component.getClientId());
                                }
                            }
                            else {
                                UIComponent component = resolver.resolveComponent(context, source, source, expression, hints);

                                if (component == null) {
                                    if (!SearchExpressionUtils.isHintSet(hints, SearchExpressionHint.IGNORE_NO_RESULT)) {
                                        cannotFindComponent(context, source, expression);
                                    }
                                }
                                else {
                                    validateRenderer(context, source, component, expression, hints);
                                    if (expressionsBuffer.length() > 0) {
                                        expressionsBuffer.append(" ");
                                    }
                                    expressionsBuffer.append(component.getClientId(context));
                                }
                            }
                        }
                        // default ID case
                        else {
                            ResolveClientIdCallback callback = new ResolveClientIdCallback(source, hints, expression);
                            resolveComponentById(source, expression, separatorString, context, callback);

                            if (callback.getClientId() == null && !SearchExpressionUtils.isHintSet(hints, SearchExpressionHint.IGNORE_NO_RESULT)) {
                                cannotFindComponent(context, source, expression);
                            }

                            if (callback.getClientId() != null) {
                                if (expressionsBuffer.length() > 0) {
                                    expressionsBuffer.append(" ");
                                }
                                expressionsBuffer.append(callback.getClientId());
                            }
                        }
                    }
                }
            }

            String clientIds = expressionsBuffer.toString();
            if (!ComponentUtils.isValueBlank(clientIds)) {
                return clientIds;
            }
        }

        return null;
    }

    protected static void validateRenderer(FacesContext context, UIComponent source, UIComponent component, String expression, int hints) {
        if (SearchExpressionUtils.isHintSet(hints, SearchExpressionHint.VALIDATE_RENDERER) && context.isProjectStage(ProjectStage.Development)) {
            if (ComponentUtils.isValueBlank(component.getRendererType())) {
                LOG.warning("Can not update component \"" + component.getClass().getName()
                        + "\" with id \"" + component.getClientId(context)
                        + "\" without a attached renderer. Expression \"" + expression
                        + "\" referenced from \"" + source.getClientId(context) + "\"");
            }
        }
    }

    /**
     * Resolves a {@link UIComponent} clientId and/or passtrough expression for the given expression.
     *
     * @param context The {@link FacesContext}.
     * @param source The source component. E.g. a button.
     * @param expression The search expression.
     * @return A resolved clientId and/or passtrough expression (like PFS, widgetVar).
     */
    public static String resolveClientId(FacesContext context, UIComponent source, String expression) {
        return resolveClientId(context, source, expression, SearchExpressionHint.NONE);
    }

    /**
     * Resolves a {@link UIComponent} clientId and/or passtrough expression for the given expression.
     *
     * @param context The {@link FacesContext}.
     * @param source The source component. E.g. a button.
     * @param expression The search expression.
     * @param hints The hints.
     * @return A resolved clientId and/or passtrough expression (like PFS, widgetVar).
     */
    public static String resolveClientId(FacesContext context, UIComponent source, String expression, int hints) {
        if (ComponentUtils.isValueBlank(expression)) {
            return null;
        }

        final char separatorChar = UINamingContainer.getSeparatorChar(context);
        final String separatorString = String.valueOf(separatorChar);

        expression = expression.trim();

        validateExpression(context, source, expression, separatorChar, separatorString);

        if (isPassTroughExpression(expression)) {
            return expression;
        }

        UIComponent component;

        // if it contains a keyword and it's not a nested expression (e.g. @parent:@parent), we don't need to loop
        if (expression.contains(SearchExpressionConstants.KEYWORD_PREFIX) && expression.contains(separatorString)) {
            component = resolveComponentByExpressionChain(context, source, expression, separatorChar, separatorString, hints);
        } // it's a keyword and not nested, just ask our resolvers
        else if (expression.contains(SearchExpressionConstants.KEYWORD_PREFIX)) {
            SearchExpressionResolver resolver = SearchExpressionResolverFactory.findResolver(expression);

            if (resolver instanceof ClientIdSearchExpressionResolver) {
                return ((ClientIdSearchExpressionResolver) resolver).resolveClientIds(context, source, source, expression, hints);
            }
            else {
                component = resolver.resolveComponent(context, source, source, expression, hints);
            }
        } // default ID case
        else {
            ResolveClientIdCallback callback = new ResolveClientIdCallback(source, hints, expression);
            resolveComponentById(source, expression, separatorString, context, callback);

            if (callback.getClientId() == null && !SearchExpressionUtils.isHintSet(hints, SearchExpressionHint.IGNORE_NO_RESULT)) {
                cannotFindComponent(context, source, expression);
            }

            return callback.getClientId();
        }

        if (component == null) {
            if (SearchExpressionUtils.isHintSet(hints, SearchExpressionHint.IGNORE_NO_RESULT)) {
                return null;
            }
            else {
                cannotFindComponent(context, source, expression);
            }
        }

        validateRenderer(context, source, component, expression, hints);

        return component.getClientId(context);
    }

    static class ResolveClientIdCallback implements ContextCallback {

        private final UIComponent source;
        private final int hints;
        private final String expression;

        private String clientId;

        ResolveClientIdCallback(UIComponent source, int hints, String expression) {
            this.source = source;
            this.hints = hints;
            this.expression = expression;
        }

        public void invokeContextCallback(FacesContext context, UIComponent target) {
            clientId = target.getClientId(context);
            validateRenderer(context, source, target, expression, hints);
        }

        public String getClientId() {
            return clientId;
        }
    }

    static class ResolveComponentCallback implements ContextCallback {

        private UIComponent component;

        public void invokeContextCallback(FacesContext context, UIComponent target) {
            component = target;
        }

        public UIComponent getComponent() {
            return component;
        }
    }

    /**
     * Resolves a {@link UIComponent} for the given expression.
     *
     * @param context The {@link FacesContext}.
     * @param source The source component. E.g. a button.
     * @param expression The search expression.
     * @return A resolved {@link UIComponent} or null.
     */
    public static UIComponent resolveComponent(FacesContext context, UIComponent source, String expression) {

        return resolveComponent(context, source, expression, SearchExpressionHint.NONE);
    }

    /**
     * Resolves a {@link UIComponent} for the given expression.
     *
     * @param context The {@link FacesContext}.
     * @param source The source component. E.g. a button.
     * @param expression The search expression.
     * @param hints The hints.
     * @return A resolved {@link UIComponent} or null.
     */
    public static UIComponent resolveComponent(FacesContext context, UIComponent source, String expression, int hints) {

        if (ComponentUtils.isValueBlank(expression)) {
            if (SearchExpressionUtils.isHintSet(hints, SearchExpressionHint.PARENT_FALLBACK)) {
                return source.getParent();
            }

            return null;
        }

        final char separatorChar = UINamingContainer.getSeparatorChar(context);
        final String separatorString = String.valueOf(separatorChar);

        expression = expression.trim();

        validateExpression(context, source, expression, separatorChar, separatorString);

        if (expression.equals(SearchExpressionConstants.NONE_KEYWORD)) {
            return null;
        }

        if (ComponentUtils.isValueBlank(expression)) {
            return null;
        }

        UIComponent component;

        // if it contains a keyword and it's not a nested expression (e.g. @parent:@parent), we don't need to loop
        if (expression.contains(SearchExpressionConstants.KEYWORD_PREFIX) && expression.contains(separatorString)) {
            component = resolveComponentByExpressionChain(context, source, expression, separatorChar, separatorString, hints);
        }
        // it's a keyword and not nested, just ask our resolvers
        else if (expression.contains(SearchExpressionConstants.KEYWORD_PREFIX)) {
            SearchExpressionResolver resolver = SearchExpressionResolverFactory.findResolver(expression);
            component = resolver.resolveComponent(context, source, source, expression, hints);
        }
        // default ID case
        else {
            ResolveComponentCallback callback = new ResolveComponentCallback();
            resolveComponentById(source, expression, separatorString, context, callback);
            component = callback.getComponent();
        }

        if (component == null && !SearchExpressionUtils.isHintSet(hints, SearchExpressionHint.IGNORE_NO_RESULT)) {
            cannotFindComponent(context, source, expression);
        }

        return component;
    }

    private static UIComponent resolveComponentByExpressionChain(FacesContext context, UIComponent source, String expression,
            char separatorChar, String separatorString, int hints) {

        boolean startsWithSeperator = expression.charAt(0) == separatorChar;

        // check if the first subExpression starts with ":",
        // this will be re-added later to the first expression (only if it's a ID expression),
        // to check if we need a absolute or relative search
        if (startsWithSeperator) {
            expression = expression.substring(1);
        }

        UIComponent last = source;

        String[] subExpressions = split(context, expression, separatorChar);
        if (subExpressions != null && subExpressions.length > 0) {
            for (int j = 0; j < subExpressions.length; j++) {

                String subExpression = subExpressions[j].trim();

                if (ComponentUtils.isValueBlank(subExpression)) {
                    continue;
                }

                // re-add the separator string here
                // the impl will decide to search absolute or relative then
                if (startsWithSeperator
                        && j == 0
                        && !subExpression.contains(SearchExpressionConstants.KEYWORD_PREFIX)) {
                    subExpression = separatorString + subExpression;
                }

                SearchExpressionResolver resolver = SearchExpressionResolverFactory.findResolver(subExpression);
                UIComponent temp = resolver.resolveComponent(context, source, last, subExpression, hints);

                if (temp == null) {
                    if (!SearchExpressionUtils.isHintSet(hints, SearchExpressionHint.IGNORE_NO_RESULT)) {
                        throw new FacesException("Cannot find component for subexpression \"" + subExpression
                                + "\" from component with id \"" + last.getClientId(context)
                                + "\" in full expression \"" + expression
                                + "\" referenced from \"" + source.getClientId(context) + "\".");
                    }

                    return null;
                }

                last = temp;
            }
        }

        return last;
    }

    private static void resolveComponentById(UIComponent source, String expression, String separatorString, FacesContext context,
            ContextCallback callback) {

        ComponentTraversalUtils.firstById(expression, source, separatorString, context, callback);
    }

    private static ArrayList resolveComponentsByExpressionChain(FacesContext context, UIComponent source, String expression, char separatorChar,
            String separatorString, int hints) {

        boolean startsWithSeperator = expression.charAt(0) == separatorChar;

        // check if the first subExpression starts with ":",
        // this will be re-added later to the first expression (only if it's a ID expression),
        // to check if we need a absolute or relative search
        if (startsWithSeperator) {
            expression = expression.substring(1);
        }

        ArrayList lastComponents = new ArrayList();
        lastComponents.add(source);

        String[] subExpressions = split(context, expression, separatorChar);
        if (subExpressions != null && subExpressions.length > 0) {

            ArrayList tempComponents = new ArrayList();

            for (int i = 0; i < subExpressions.length; i++) {

                String subExpression = subExpressions[i].trim();

                if (ComponentUtils.isValueBlank(subExpression)) {
                    continue;
                }

                // re-add the separator string here
                // the impl will decide to search absolute or relative then
                if (startsWithSeperator
                        && i == 0
                        && !subExpression.contains(SearchExpressionConstants.KEYWORD_PREFIX)) {
                    subExpression = separatorString + subExpression;
                }

                SearchExpressionResolver resolver = SearchExpressionResolverFactory.findResolver(subExpression);

                tempComponents.clear();

                for (int j = 0; j < lastComponents.size(); j++) {
                    UIComponent last = lastComponents.get(j);

                    if (resolver instanceof MultiSearchExpressionResolver) {
                        ((MultiSearchExpressionResolver) resolver).resolveComponents(context, source, last, subExpression, tempComponents, hints);
                    }
                    else {
                        UIComponent temp = resolver.resolveComponent(context, source, last, subExpression, hints);

                        if (temp == null) {
                            if (!SearchExpressionUtils.isHintSet(hints, SearchExpressionHint.IGNORE_NO_RESULT)) {
                                throw new FacesException("Cannot find component for subexpression \"" + subExpression
                                        + "\" from component with id \"" + last.getClientId(context)
                                        + "\" in full expression \"" + expression
                                        + "\" referenced from \"" + source.getClientId(context) + "\".");
                            }
                        }
                        else {
                            tempComponents.add(temp);
                        }
                    }
                }

                lastComponents.clear();
                lastComponents.addAll(tempComponents);
                tempComponents.clear();
            }
        }

        return lastComponents;
    }

    private static String resolveClientIdsByExpressionChain(FacesContext context, UIComponent source, String expression, char separatorChar,
            String separatorString, int hints) {

        boolean startsWithSeperator = expression.charAt(0) == separatorChar;

        // check if the first subExpression starts with ":",
        // this will be re-added later to the first expression (only if it's a ID expression),
        // to check if we need a absolute or relative search
        if (startsWithSeperator) {
            expression = expression.substring(1);
        }

        ArrayList lastComponents = new ArrayList();
        lastComponents.add(source);

        StringBuilder clientIdsBuilder = null;

        String[] subExpressions = split(context, expression, separatorChar);
        if (subExpressions != null && subExpressions.length > 0) {

            ArrayList tempComponents = new ArrayList();

            for (int i = 0; i < subExpressions.length; i++) {

                String subExpression = subExpressions[i].trim();

                if (ComponentUtils.isValueBlank(subExpression)) {
                    continue;
                }

                // re-add the separator string here
                // the impl will decide to search absolute or relative then
                if (startsWithSeperator
                        && i == 0
                        && !subExpression.contains(SearchExpressionConstants.KEYWORD_PREFIX)) {
                    subExpression = separatorString + subExpression;
                }

                SearchExpressionResolver resolver = SearchExpressionResolverFactory.findResolver(subExpression);

                tempComponents.clear();

                for (int j = 0; j < lastComponents.size(); j++) {
                    UIComponent last = lastComponents.get(j);

                    // if it's the last expression and the resolver is a ClientIdSearchExpressionResolver, we can call it
                    if (i == subExpressions.length - 1 && resolver instanceof ClientIdSearchExpressionResolver) {
                        String result = ((ClientIdSearchExpressionResolver) resolver).resolveClientIds(context, source, last, subExpression, hints);

                        if (!ComponentUtils.isValueBlank(result)) {

                            if (clientIdsBuilder == null) {
                                clientIdsBuilder = SharedStringBuilder.get(SHARED_CLIENT_ID_EXPRESSION_BUFFER_KEY);
                            }
                            else if (clientIdsBuilder.length() > 0) {
                                clientIdsBuilder.append(" ");
                            }

                            clientIdsBuilder.append(result);
                        }
                    }
                    else if (resolver instanceof MultiSearchExpressionResolver) {
                        ((MultiSearchExpressionResolver) resolver).resolveComponents(context, source, last, subExpression, tempComponents, hints);
                    }
                    else {
                        UIComponent temp = resolver.resolveComponent(context, source, last, subExpression, hints);

                        if (temp == null) {
                            if (!SearchExpressionUtils.isHintSet(hints, SearchExpressionHint.IGNORE_NO_RESULT)) {
                                throw new FacesException("Cannot find component for subexpression \"" + subExpression
                                        + "\" from component with id \"" + last.getClientId(context)
                                        + "\" in full expression \"" + expression
                                        + "\" referenced from \"" + source.getClientId(context) + "\".");
                            }
                        }
                        else {
                            tempComponents.add(temp);
                        }
                    }
                }

                lastComponents.clear();
                lastComponents.addAll(tempComponents);
                tempComponents.clear();
            }
        }

        // already initialized -> last resolver was a ClientIdExpressionResolver
        if (clientIdsBuilder == null) {
            clientIdsBuilder = SharedStringBuilder.get(SHARED_CLIENT_ID_EXPRESSION_BUFFER_KEY);

            for (int i = 0; i < lastComponents.size(); i++) {
                UIComponent result = lastComponents.get(i);
                if (clientIdsBuilder.length() > 0) {
                    clientIdsBuilder.append(" ");
                }
                clientIdsBuilder.append(result.getClientId(context));
            }

        }

        return clientIdsBuilder.toString();
    }

    protected static void cannotFindComponent(FacesContext context, UIComponent source, String expression) {
        throw new ComponentNotFoundException("Cannot find component for expression \""
                + expression + "\" referenced from \""
                + source.getClientId(context) + "\".");
    }

    protected static String[] splitExpressions(FacesContext context, UIComponent source, String expressions) {

        // split expressions by blank or comma (and ignore blank and commas inside brackets)
        String[] splittedExpressions = split(context, expressions, EXPRESSION_SEPARATORS);

        if (splittedExpressions != null) {

            validateExpressions(context, source, expressions, splittedExpressions);
        }

        return splittedExpressions;
    }

    /**
     * Validates the given search expression. We only validate it, for performance reasons, if the current {@link ProjectStage} is
     * {@link ProjectStage#Development}.
     *
     * @param context The {@link FacesContext}.
     * @param source The source component. E.g. a button.
     * @param expression The search expression.
     * @param separatorChar The separator as char.
     * @param separatorString The separator as string.
     */
    protected static void validateExpression(FacesContext context, UIComponent source,
            String expression, char separatorChar, String separatorString) {

        if (context.isProjectStage(ProjectStage.Development)) {

            // checks the whole expression doesn't start with ":@"
            // keywords are always related to the current component, not absolute or relative
            if (expression.startsWith(separatorString + SearchExpressionConstants.KEYWORD_PREFIX)) {
                throw new FacesException("A expression should not start with the separater char and a keyword. "
                        + "Expression: \"" + expression + "\" referenced from \"" + source.getClientId(context) + "\"");
            }

            // Pattern to split expressions by the separator but not inside parenthesis
            String[] subExpressions = split(context, expression, separatorChar);

            if (subExpressions != null) {
                // checks for unnestable subexpressions (like @all or @none)
                if (subExpressions.length > 1) {
                    for (String subExpression : subExpressions) {
                        subExpression = subExpression.trim();

                        if (!isNestable(subExpression)) {
                            throw new FacesException("Subexpression \"" + subExpression
                                    + "\" in full expression \"" + expression
                                    + "\" from \"" + source.getClientId(context) + "\" can not be nested.");
                        }
                    }
                }
            }
        }
    }

    /**
     * Validates the given search expressions. We only validate it, for performance reasons, if the current {@link ProjectStage} is
     * {@link ProjectStage#Development}.
     *
     * @param context The {@link FacesContext}.
     * @param source The source component. E.g. a button.
     * @param expressions The search expression.
     * @param splittedExpressions The already splitted expressions.
     */
    protected static void validateExpressions(FacesContext context, UIComponent source, String expressions, String[] splittedExpressions) {

        if (context.isProjectStage(ProjectStage.Development)) {
            if (splittedExpressions.length > 1) {
                if (expressions.contains(SearchExpressionConstants.NONE_KEYWORD)
                        || expressions.contains(SearchExpressionConstants.ALL_KEYWORD)) {

                    throw new FacesException("It's not possible to use @none or @all combined with other expressions."
                            + " Expressions: \"" + expressions
                            + "\" referenced from \"" + source.getClientId(context) + "\"");
                }
            }
        }
    }

    /**
     * Splits the given string by the given separator, but ignoring separators inside parentheses.
     *
     * @param context The current {@link FacesContext}.
     * @param value The string value.
     * @param separators The separators.
     * @return The splitted string.
     */
    protected static String[] split(FacesContext context, String value, char... separators) {

        if (value == null) {
            return null;
        }

        List tokens = new ArrayList();
        StringBuilder buffer = SharedStringBuilder.get(context, SHARED_SPLIT_BUFFER_KEY);

        int parenthesesCounter = 0;

        char[] charArray = value.toCharArray();

        for (char c : charArray) {
            if (c == '(') {
                parenthesesCounter++;
            }

            if (c == ')') {
                parenthesesCounter--;
            }

            if (parenthesesCounter == 0) {
                boolean isSeparator = false;
                for (char separator : separators) {
                    if (c == separator) {
                        isSeparator = true;
                    }
                }

                if (isSeparator) {
                    // lets add token inside buffer to our tokens
                    tokens.add(buffer.toString());
                    // now we need to clear buffer
                    buffer.delete(0, buffer.length());
                }
                else {
                    buffer.append(c);
                }
            }
            else {
                buffer.append(c);
            }
        }

        // lets not forget about part after the separator
        tokens.add(buffer.toString());

        return tokens.toArray(new String[tokens.size()]);
    }

    /**
     * Checks if the given expression must not be resolved by a {@link SearchExpressionResolver}, before rendering it to the client. e.g. @all or
     * @none.
     *
     * @param expression The search expression.
     * @return true if it should just be rendered without manipulation or resolving.
     */
    protected static boolean isPassTroughExpression(String expression) {
        return expression.contains(SearchExpressionConstants.PFS_PREFIX);
    }

    /**
     * Checks if the given expression can be nested. e.g. @form:@parent This should not be possible e.g. with @none or @all.
     *
     * @param expression The search expression.
     * @return true if it's nestable.
     */
    protected static boolean isNestable(String expression) {
        return !(expression.contains(SearchExpressionConstants.ALL_KEYWORD)
                || expression.contains(SearchExpressionConstants.NONE_KEYWORD)
                || expression.contains(SearchExpressionConstants.PFS_PREFIX));
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy