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

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

There is a newer version: 14.0.0-RC3
Show newest version
/*
 * Copyright 2009-2014 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