You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by lu...@apache.org on 2016/11/23 01:36:27 UTC

svn commit: r1770904 [2/3] - in /myfaces/core/branches/2.3.x: api/src/main/java/javax/faces/ api/src/main/java/javax/faces/application/ api/src/main/java/javax/faces/component/search/ impl/src/main/java/org/apache/myfaces/application/ impl/src/main/jav...

Added: myfaces/core/branches/2.3.x/impl/src/main/java/org/apache/myfaces/component/search/SearchComponentUtils.java
URL: http://svn.apache.org/viewvc/myfaces/core/branches/2.3.x/impl/src/main/java/org/apache/myfaces/component/search/SearchComponentUtils.java?rev=1770904&view=auto
==============================================================================
--- myfaces/core/branches/2.3.x/impl/src/main/java/org/apache/myfaces/component/search/SearchComponentUtils.java (added)
+++ myfaces/core/branches/2.3.x/impl/src/main/java/org/apache/myfaces/component/search/SearchComponentUtils.java Wed Nov 23 01:36:27 2016
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.apache.myfaces.component.search;
+
+import javax.faces.component.UIComponent;
+
+/**
+ *
+ */
+public class SearchComponentUtils
+{
+    
+    public static UIComponent getRootComponent(UIComponent component)
+    {
+        UIComponent parent;
+        for (;;)
+        {
+            parent = component.getParent();
+            if (parent == null)
+            {
+                return component;
+            }
+            component = parent;
+        }
+    }
+}

Added: myfaces/core/branches/2.3.x/impl/src/main/java/org/apache/myfaces/component/search/SearchExpressionContextFactoryImpl.java
URL: http://svn.apache.org/viewvc/myfaces/core/branches/2.3.x/impl/src/main/java/org/apache/myfaces/component/search/SearchExpressionContextFactoryImpl.java?rev=1770904&view=auto
==============================================================================
--- myfaces/core/branches/2.3.x/impl/src/main/java/org/apache/myfaces/component/search/SearchExpressionContextFactoryImpl.java (added)
+++ myfaces/core/branches/2.3.x/impl/src/main/java/org/apache/myfaces/component/search/SearchExpressionContextFactoryImpl.java Wed Nov 23 01:36:27 2016
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.apache.myfaces.component.search;
+
+import java.util.HashSet;
+import java.util.Set;
+import javax.faces.component.UIComponent;
+import javax.faces.component.search.SearchExpressionContext;
+import javax.faces.component.search.SearchExpressionContextFactory;
+import javax.faces.component.search.SearchExpressionHint;
+import javax.faces.component.visit.VisitHint;
+import javax.faces.context.FacesContext;
+
+/**
+ *
+ */
+public class SearchExpressionContextFactoryImpl extends SearchExpressionContextFactory
+{
+
+    @Override
+    public SearchExpressionContext getSearchExpressionContext(
+            FacesContext context, UIComponent source, 
+            Set<SearchExpressionHint> expressionHints, Set<VisitHint> visitHints)
+    {
+        SearchExpressionContextImpl searchExpressionContext = new SearchExpressionContextImpl(context);
+        searchExpressionContext.setSource(source);
+        searchExpressionContext.setExpressionHints(expressionHints == null ? 
+                new HashSet<SearchExpressionHint>(2) : expressionHints);
+        searchExpressionContext.setVisitHints(visitHints);
+        return searchExpressionContext;
+    }
+    
+}

Added: myfaces/core/branches/2.3.x/impl/src/main/java/org/apache/myfaces/component/search/SearchExpressionContextImpl.java
URL: http://svn.apache.org/viewvc/myfaces/core/branches/2.3.x/impl/src/main/java/org/apache/myfaces/component/search/SearchExpressionContextImpl.java?rev=1770904&view=auto
==============================================================================
--- myfaces/core/branches/2.3.x/impl/src/main/java/org/apache/myfaces/component/search/SearchExpressionContextImpl.java (added)
+++ myfaces/core/branches/2.3.x/impl/src/main/java/org/apache/myfaces/component/search/SearchExpressionContextImpl.java Wed Nov 23 01:36:27 2016
@@ -0,0 +1,103 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.apache.myfaces.component.search;
+
+import java.util.Set;
+import javax.faces.component.UIComponent;
+import javax.faces.component.search.SearchExpressionContext;
+import javax.faces.component.search.SearchExpressionHint;
+import javax.faces.component.visit.VisitHint;
+import javax.faces.context.FacesContext;
+
+/**
+ *
+ */
+public class SearchExpressionContextImpl extends SearchExpressionContext
+{
+    private final FacesContext facesContext;
+    
+    private UIComponent source;
+
+    private Set<VisitHint> visitHints;
+    
+    private Set<SearchExpressionHint> expressionHints;
+    
+    public SearchExpressionContextImpl(FacesContext facesContext)
+    {
+        this.facesContext = facesContext;
+    }
+
+    /**
+     * @return the source
+     */
+    public UIComponent getSource()
+    {
+        return source;
+    }
+
+    /**
+     * @param source the source to set
+     */
+    public void setSource(UIComponent source)
+    {
+        this.source = source;
+    }
+
+    /**
+     * @return the visitHints
+     */
+    public Set<VisitHint> getVisitHints()
+    {
+        return visitHints;
+    }
+
+    /**
+     * @param visitHints the visitHints to set
+     */
+    public void setVisitHints(Set<VisitHint> visitHints)
+    {
+        this.visitHints = visitHints;
+    }
+
+    /**
+     * @return the expressionHints
+     */
+    public Set<SearchExpressionHint> getExpressionHints()
+    {
+        return expressionHints;
+    }
+
+    /**
+     * @param expressionHints the expressionHints to set
+     */
+    public void setExpressionHints(Set<SearchExpressionHint> expressionHints)
+    {
+        this.expressionHints = expressionHints;
+    }
+
+    /**
+     * @return the facesContext
+     */
+    public FacesContext getFacesContext()
+    {
+        return facesContext;
+    }
+
+}

Added: myfaces/core/branches/2.3.x/impl/src/main/java/org/apache/myfaces/component/search/SearchExpressionHandlerImpl.java
URL: http://svn.apache.org/viewvc/myfaces/core/branches/2.3.x/impl/src/main/java/org/apache/myfaces/component/search/SearchExpressionHandlerImpl.java?rev=1770904&view=auto
==============================================================================
--- myfaces/core/branches/2.3.x/impl/src/main/java/org/apache/myfaces/component/search/SearchExpressionHandlerImpl.java (added)
+++ myfaces/core/branches/2.3.x/impl/src/main/java/org/apache/myfaces/component/search/SearchExpressionHandlerImpl.java Wed Nov 23 01:36:27 2016
@@ -0,0 +1,1028 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.apache.myfaces.component.search;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import javax.faces.component.ContextCallback;
+import javax.faces.component.NamingContainer;
+import javax.faces.component.UIComponent;
+import javax.faces.component.search.ComponentNotFoundException;
+import javax.faces.component.search.SearchExpressionContext;
+import javax.faces.component.search.SearchExpressionHandler;
+import javax.faces.component.search.SearchExpressionHint;
+import javax.faces.component.search.SearchKeywordContext;
+import javax.faces.context.FacesContext;
+
+/**
+ *
+ */
+public class SearchExpressionHandlerImpl extends SearchExpressionHandler
+{
+    
+    public String resolveClientId(SearchExpressionContext searchExpressionContext, String expression)
+    {
+        FacesContext facesContext = searchExpressionContext.getFacesContext();
+        UIComponent source = searchExpressionContext.getSource();
+        CollectClientIdCallback callback = new CollectClientIdCallback();
+        Set<SearchExpressionHint> hints = searchExpressionContext.getExpressionHints();
+        hints.add(SearchExpressionHint.RESOLVE_CLIENT_ID);
+        SearchExpressionHandler handler = facesContext.getApplication().getSearchExpressionHandler();
+        if (handler.isPassthroughExpression(searchExpressionContext, expression))
+        {
+            return expression;
+        }
+        else
+        {
+            handler.invokeOnComponentFromExpression(
+                    searchExpressionContext, searchExpressionContext.getSource(), expression, callback);
+            
+            if (!callback.isClientIdFound() && hints != null && hints.contains(SearchExpressionHint.PARENT_FALLBACK))
+            {
+                callback.invokeContextCallback(facesContext, source.getParent());
+            }
+            if (!callback.isClientIdFound())
+            {
+                if (hints != null && hints.contains(SearchExpressionHint.IGNORE_NO_RESULT))
+                {
+                    //Ignore
+                }
+                else
+                {
+                    throw new ComponentNotFoundException("Cannot find component for expression \""
+                        + expression + "\" referenced from \""
+                        + searchExpressionContext.getSource().getClientId(facesContext) + "\".");
+                }
+            }
+            return callback.getClientId();
+        }
+    }
+    
+    private static class CollectClientIdCallback implements ContextCallback
+    {
+        private String clientId = null;
+
+        @Override
+        public void invokeContextCallback(FacesContext context, UIComponent target)
+        {
+            if (clientId == null)
+            {
+                clientId = target.getClientId(context);
+            }
+        }
+        
+        private String getClientId()
+        {
+            return clientId;
+        }
+        
+        private boolean isClientIdFound()
+        {
+            return clientId != null;
+        }
+    }
+
+    public List<String> resolveClientIds(SearchExpressionContext searchExpressionContext,  
+            String expressions)
+    {
+        FacesContext facesContext = searchExpressionContext.getFacesContext();
+        UIComponent source = searchExpressionContext.getSource();
+        CollectClientIdsCallback callback = new CollectClientIdsCallback();
+        Set<SearchExpressionHint> hints = searchExpressionContext.getExpressionHints();
+        hints.add(SearchExpressionHint.RESOLVE_CLIENT_ID);
+        SearchExpressionHandler handler = facesContext.getApplication().getSearchExpressionHandler();
+        for (String expression : 
+                facesContext.getApplication().getSearchExpressionHandler().splitExpressions(expressions))
+        {
+            if (handler.isPassthroughExpression(searchExpressionContext, expression))
+            {
+                // It will be resolved in the client, just add the expression.
+                callback.addClientId(expression);
+            }
+            else
+            {
+                handler.invokeOnComponentFromExpression(
+                        searchExpressionContext, searchExpressionContext.getSource(), expression, callback);
+            }
+        }
+        if (!callback.isClientIdFound() && hints != null && hints.contains(SearchExpressionHint.PARENT_FALLBACK))
+        {
+            callback.invokeContextCallback(facesContext, source.getParent());
+        }
+        if (!callback.isClientIdFound())
+        {
+            if (hints != null && hints.contains(SearchExpressionHint.IGNORE_NO_RESULT))
+            {
+                //Ignore
+            }
+            else
+            {
+                throw new ComponentNotFoundException("Cannot find component for expression \""
+                    + expressions + "\" referenced from \""
+                    + searchExpressionContext.getSource().getClientId(facesContext) + "\".");
+            }
+        }
+        return callback.getClientIds();
+    }
+    
+    private static class CollectClientIdsCallback implements ContextCallback
+    {
+        private List<String> clientIds = null;
+
+        @Override
+        public void invokeContextCallback(FacesContext context, UIComponent target)
+        {
+            if (clientIds == null)
+            {
+                clientIds = new ArrayList<String>();
+            }
+            clientIds.add(target.getClientId(context));
+        }
+        
+        private List<String> getClientIds()
+        {
+            return clientIds == null ? Collections.emptyList() : clientIds;
+        }
+        
+        private boolean isClientIdFound()
+        {
+            return clientIds != null;
+        }
+        
+        private void addClientId(String clientId)
+        {
+            if (clientIds == null)
+            {
+                clientIds = new ArrayList<String>();
+            }
+            clientIds.add(clientId);
+        }
+    }
+    
+    public void resolveComponent(SearchExpressionContext searchExpressionContext, String expression, 
+        ContextCallback callback)
+    {
+        FacesContext facesContext = searchExpressionContext.getFacesContext();
+        SingleInvocationCallback checkCallback = new SingleInvocationCallback(callback);
+        Set<SearchExpressionHint> hints = searchExpressionContext.getExpressionHints();
+        facesContext.getApplication().getSearchExpressionHandler().invokeOnComponentFromExpression(
+                searchExpressionContext, searchExpressionContext.getSource(), expression, checkCallback);
+        
+        if (!checkCallback.isInvoked() && hints != null && hints.contains(SearchExpressionHint.PARENT_FALLBACK))
+        {
+            checkCallback.invokeContextCallback(facesContext, searchExpressionContext.getSource().getParent());
+        }
+        if (!checkCallback.isInvoked())
+        {
+            if (hints != null && hints.contains(SearchExpressionHint.IGNORE_NO_RESULT))
+            {
+                //Ignore
+            }
+            else
+            {
+                throw new ComponentNotFoundException("Cannot find component for expression \""
+                    + expression + "\" referenced from \""
+                    + searchExpressionContext.getSource().getClientId(facesContext) + "\".");
+            }
+        }
+    }
+    
+    private static class SingleInvocationCallback implements ContextCallback
+    {
+        private boolean invoked;
+        
+        private final ContextCallback innerCallback;
+
+        public SingleInvocationCallback(ContextCallback innerCallback)
+        {
+            this.innerCallback = innerCallback;
+            this.invoked = false;
+        }
+
+        @Override
+        public void invokeContextCallback(FacesContext context, UIComponent target)
+        {
+            if (!isInvoked())
+            {
+                try
+                {
+                    innerCallback.invokeContextCallback(context, target);
+                }
+                finally
+                {
+                    invoked = true;
+                }
+            }
+        }
+
+        public boolean isInvoked()
+        {
+            return invoked;
+        }
+    }
+
+    public void resolveComponents(SearchExpressionContext searchExpressionContext, String expressions, 
+        ContextCallback callback)
+    {
+        FacesContext facesContext = searchExpressionContext.getFacesContext();
+        MultipleInvocationCallback checkCallback = new MultipleInvocationCallback(callback);
+        Set<SearchExpressionHint> hints = searchExpressionContext.getExpressionHints();
+        for (String expression : 
+                facesContext.getApplication().getSearchExpressionHandler().splitExpressions(expressions))
+        {
+            facesContext.getApplication().getSearchExpressionHandler().invokeOnComponentFromExpression(
+                    searchExpressionContext, searchExpressionContext.getSource(), expression, checkCallback);
+        }
+        // ...
+        if (!checkCallback.isInvoked() && hints != null && hints.contains(SearchExpressionHint.PARENT_FALLBACK))
+        {
+            checkCallback.invokeContextCallback(facesContext, searchExpressionContext.getSource().getParent());
+        }
+        if (!checkCallback.isInvoked())
+        {
+            if (hints != null && hints.contains(SearchExpressionHint.IGNORE_NO_RESULT))
+            {
+                //Ignore
+            }
+            else
+            {
+                throw new ComponentNotFoundException("Cannot find component for expression \""
+                    + expressions + "\" referenced from \""
+                    + searchExpressionContext.getSource().getClientId(facesContext) + "\".");
+            }
+        }
+    }
+    
+    private static class MultipleInvocationCallback implements ContextCallback
+    {
+        private boolean invoked;
+        
+        private final ContextCallback innerCallback;
+
+        public MultipleInvocationCallback(ContextCallback innerCallback)
+        {
+            this.innerCallback = innerCallback;
+            this.invoked = false;
+        }
+
+        @Override
+        public void invokeContextCallback(FacesContext context, UIComponent target)
+        {
+            try
+            {
+                innerCallback.invokeContextCallback(context, target);
+            }
+            finally
+            {
+                invoked = true;
+            }
+        }
+
+        public boolean isInvoked()
+        {
+            return invoked;
+        }
+    }
+
+    public void invokeOnComponentFromExpression(final SearchExpressionContext searchExpressionContext, 
+            UIComponent source, String topExpression, ContextCallback topCallback)
+    {
+        // Command pattern to apply the keyword or command to the base and then invoke the callback
+        FacesContext facesContext = searchExpressionContext.getFacesContext();
+        
+        UIComponent currentBase = source;
+        
+        //Step 1: find base        
+        //  Case ':' (root)
+        char separatorChar = facesContext.getNamingContainerSeparatorChar();
+        if (topExpression.charAt(0) == separatorChar)
+        {
+            UIComponent findBase;
+            findBase = SearchComponentUtils.getRootComponent(currentBase);
+            facesContext.getApplication().getSearchExpressionHandler().invokeOnComponentFromExpression(
+                    searchExpressionContext, findBase, topExpression.substring(1), topCallback);
+            return;
+        }
+
+        //Step 2: Once you have a base where you can start, apply an expression 
+        if (topExpression.charAt(0) == KEYWORD_PREFIX.charAt(0))
+        {
+            // A keyword means apply a command over the current source using an expression and the result must be
+            // feedback into the algorithm.
+            final UIComponent base = currentBase;
+
+            String command = extractKeyword(topExpression, 1, separatorChar);
+            final String remaining = 
+                    command.length()+1 < topExpression.length() ? 
+                        topExpression.substring(1+command.length()+1) : null;
+            
+            final ContextCallback parentCallback = topCallback;
+            final SearchExpressionHandler currentInstance = 
+                    facesContext.getApplication().getSearchExpressionHandler();
+
+            // If the keyword is @child, @composite, @form, @namingcontainer, @next, @none, @parent, @previous,
+            // @root, @this ,  all commands change the source to be applied the action
+            if (remaining != null)
+            {
+                this.applyKeyword(searchExpressionContext, base, command, new ContextCallback()
+                    {
+                        @Override
+                        public void invokeContextCallback(FacesContext facesContext, UIComponent target)
+                        {
+                            currentInstance.invokeOnComponentFromExpression(
+                                    searchExpressionContext, target, remaining, parentCallback);
+                        }
+                    });
+            }
+            else
+            {
+                // Command completed, apply parent callback
+                this.applyKeyword(searchExpressionContext, base, command, parentCallback);
+            }
+        }
+        else
+        {
+
+            //Split expression into tokens and apply loop
+            String nextExpression = null;
+            String expression;
+            if (topExpression.indexOf(":@") > 0)
+            {
+                int idx = topExpression.indexOf(":@");
+                nextExpression = topExpression.substring(idx+1);
+                expression = topExpression.substring(0,idx);
+            }
+            else
+            {
+                expression = topExpression;
+            }
+
+            // Use findComponent(...) passing the expression provided
+            UIComponent target = currentBase.findComponent(expression);
+            if (target == null)
+            {
+                // If no component is found ...
+                // First try to find the base component.
+
+                // Extract the base id from the expression string
+                int idx = expression.indexOf(separatorChar);
+                String base = idx > 0 ? expression.substring(0, idx) : expression;
+
+                // From the context component clientId, check if the base is part of the clientId
+                String contextClientId = currentBase.getClientId(facesContext);
+                int startCommon = contextClientId.lastIndexOf(base+facesContext.getNamingContainerSeparatorChar());
+                if (startCommon >= 0 
+                    && (startCommon == 0 || contextClientId.charAt(startCommon-1) == separatorChar )
+                    && (startCommon+base.length() <= contextClientId.length()-1 || 
+                        contextClientId.charAt(startCommon+base.length()+1) == separatorChar )) 
+                {
+                    // If there is a match, try to find a the first parent component whose id is equals to
+                    // the base id
+                    UIComponent parent = currentBase;
+                    while (parent != null )
+                    {
+                        if (base.equals(parent.getId()) && parent instanceof NamingContainer)
+                        {
+                            break;
+                        }
+                        else
+                        {
+                            parent = parent.getParent();
+                        }
+                    }
+
+                    // if a base component is found ...
+                    if (parent != null)
+                    {
+                        target = parent.findComponent(expression);
+                        if (target == null)
+                        {
+                            contextClientId = parent.getClientId(facesContext);
+                            // If no component is found,
+                            String targetClientId = contextClientId.substring(0, startCommon+base.length()) + 
+                                    expression.substring(base.length());
+
+                            final SearchExpressionHandler currentHandler = 
+                                    facesContext.getApplication().getSearchExpressionHandler();
+
+                            if (nextExpression != null)
+                            {
+                                final String childExpression = nextExpression;
+
+                                parent.invokeOnComponent(facesContext, targetClientId, new ContextCallback(){
+                                    public void invokeContextCallback(FacesContext context, UIComponent target)
+                                    {
+                                        currentHandler.invokeOnComponentFromExpression(
+                                                searchExpressionContext, target, childExpression, topCallback);
+                                    }
+                                });
+                            }
+                            else
+                            {
+                                parent.invokeOnComponent(facesContext, targetClientId, topCallback);
+                            }
+                            return;
+                        }
+                    }
+                }
+            }
+            if (target != null)
+            {
+                currentBase = target;
+            }
+            if (currentBase != null)
+            {
+                topCallback.invokeContextCallback(facesContext, currentBase);
+            }
+        }
+    }
+    
+    public void applyKeyword(SearchExpressionContext searchExpressionContext, UIComponent last, 
+                             String command, ContextCallback topCallback)
+    {
+        // take the command and resolve it using the chain of responsibility pattern.
+        SearchKeywordContext searchContext = new SearchKeywordContext(searchExpressionContext.getFacesContext());
+        searchContext.setSearchExpressionContext(searchExpressionContext);
+        searchContext.setTopCallback(topCallback);
+        searchExpressionContext.getFacesContext().getApplication()
+                .getSearchExpressionResolver().resolve(searchContext, last, command);
+    }
+    
+    public boolean isPassthroughExpression(SearchExpressionContext searchExpressionContext, String topExpression)
+    {
+        FacesContext facesContext = searchExpressionContext.getFacesContext();
+        // Command pattern to apply the keyword or command to the base and then invoke the callback
+        boolean passthrough = false;
+        //Step 1: find base        
+        //  Case ':' (root)
+        char separatorChar = facesContext.getNamingContainerSeparatorChar();
+        if (topExpression.charAt(0) == separatorChar)
+        {
+            //return facesContext.getApplication().getSearchExpressionHandler().isPassthroughExpression(
+            //        searchExpressionContext, topExpression.substring(1));
+            // only keywords are passthrough expressions.
+            return false;
+        }
+
+        //Step 2: Once you have a base where you can start, apply an expression 
+        if (topExpression.charAt(0) == KEYWORD_PREFIX.charAt(0))
+        {
+            // A keyword means apply a command over the current source using an expression and the result must be
+            // feedback into the algorithm.
+
+            String command = extractKeyword(topExpression, 1, separatorChar);
+            final String remaining = 
+                    command.length()+1 < topExpression.length() ? 
+                        topExpression.substring(1+command.length()+1) : null;
+            
+            final SearchExpressionHandler currentInstance = 
+                    facesContext.getApplication().getSearchExpressionHandler();
+
+            // If the keyword is @child, @composite, @form, @namingcontainer, @next, @none, @parent, @previous,
+            // @root, @this ,  all commands change the source to be applied the action
+            passthrough = facesContext.getApplication().getSearchExpressionResolver().isPassthroughKeyword(
+                    searchExpressionContext, command);
+            
+            if (passthrough)
+            {
+                return remaining != null ? 
+                        currentInstance.isPassthroughExpression(searchExpressionContext, remaining) : true;
+            }
+            else
+            {
+                return false;
+            }
+        }
+        else
+        {
+            // Only keywords are valid to be passthrough. If it contains a chain of ids, this can only be resolved
+            // server side, because the tree structure and related clientId logic is only available server side.
+            return false;
+        }
+    }
+    
+    public boolean isValidExpression(SearchExpressionContext searchExpressionContext, String topExpression)
+    {
+        FacesContext facesContext = searchExpressionContext.getFacesContext();
+        // Command pattern to apply the keyword or command to the base and then invoke the callback
+        boolean isValid = true;
+        //Step 1: find base        
+        //  Case ':' (root)
+        char separatorChar = facesContext.getNamingContainerSeparatorChar();
+        if (topExpression.charAt(0) == separatorChar)
+        {
+            return facesContext.getApplication().getSearchExpressionHandler().isValidExpression(
+                    searchExpressionContext, topExpression.substring(1));
+        }
+
+        //Step 2: Once you have a base where you can start, apply an expression 
+        if (topExpression.charAt(0) == KEYWORD_PREFIX.charAt(0))
+        {
+            // A keyword means apply a command over the current source using an expression and the result must be
+            // feedback into the algorithm.
+
+            String command = extractKeyword(topExpression, 1, separatorChar);
+            final String remaining = 
+                    command.length()+1 < topExpression.length() ? 
+                        topExpression.substring(1+command.length()+1) : null;
+            
+            final SearchExpressionHandler currentInstance = 
+                    facesContext.getApplication().getSearchExpressionHandler();
+
+            // If the keyword is @child, @composite, @form, @namingcontainer, @next, @none, @parent, @previous,
+            // @root, @this ,  all commands change the source to be applied the action
+            isValid = facesContext.getApplication().getSearchExpressionResolver().matchKeyword(
+                    searchExpressionContext, command);
+            if (remaining != null)
+            {
+                if (facesContext.getApplication().getSearchExpressionResolver().isLeafKeyword(
+                    searchExpressionContext, command))
+                {
+                    isValid = false;
+                }
+                return !isValid ? false : currentInstance.isValidExpression(searchExpressionContext, remaining);
+            }
+        }
+        else
+        {
+            //Split expression into tokens and apply loop
+            String nextExpression = null;
+            String expression = null;
+            if (topExpression.indexOf(":@") > 0)
+            {
+                int idx = topExpression.indexOf(":@");
+                nextExpression = topExpression.substring(idx+1);
+                expression = topExpression.substring(0,idx);
+            }
+            else
+            {
+                expression = topExpression;
+            }
+
+            //Check expression 
+            for (int i = 0; i < expression.length(); i++)
+            {
+                char c = expression.charAt(i);
+                if (Character.isLetterOrDigit(c) || c == '-' || c == '_' || c == separatorChar)
+                {
+                    //continue
+                }
+                else
+                {
+                    isValid = false;
+                }
+            }
+            
+            if (nextExpression != null)
+            {
+                return !isValid ? false : facesContext.getApplication().getSearchExpressionHandler()
+                    .isValidExpression(searchExpressionContext, nextExpression);
+            }
+        }
+        return isValid;
+    }
+    
+    private static String[] splitSingleExpression(String value)
+    {
+        if (value == null)
+        {
+            return null;
+        }
+
+        List<String> tokens = new ArrayList<String>();
+        StringBuilder buffer = new StringBuilder();
+
+        int parenthesesCounter = 0;
+
+        char[] charArray = value.toCharArray();
+
+        for (int i = 0; i < charArray.length; i++)
+        {
+            char c = charArray[i];
+            if (c == '(')
+            {
+                parenthesesCounter++;
+            }
+
+            if (c == ')')
+            {
+                parenthesesCounter--;
+            }
+
+            if (parenthesesCounter == 0)
+            {
+                boolean isSeparator = false;
+                
+                if (c == ':')
+                {
+                    for (int j = i+1; j < charArray.length; j++)
+                    {
+                        if (charArray[j] == '@')
+                        {
+                            isSeparator = true;
+                            break;
+                        }
+                        else if (Character.isAlphabetic(charArray[j]))
+                        {
+                            //Different char than @
+                            break;
+                        }
+                    }
+                }
+
+                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()]);
+    }
+    
+    private static class ComponentExistsContextCallback implements ContextCallback
+    {
+        private boolean found;
+
+        /**
+         * @return the found
+         */
+        public boolean isFound()
+        {
+            return found;
+        }
+
+        /**
+         * @param found the found to set
+         */
+        public void setFound(boolean found)
+        {
+            this.found = found;
+        }
+
+        @Override
+        public void invokeContextCallback(FacesContext context, UIComponent target)
+        {
+            found = true;
+        }
+    }
+    
+    private static String extractKeyword(String expression, int startIndex, char separatorChar)
+    {
+        int parenthesesCounter = -1;
+        int count = -1;
+        for (int i = startIndex; i < expression.length(); i++)
+        {
+            char c = expression.charAt(i);
+            if (c == '(')
+            {
+                if (parenthesesCounter == -1)
+                {
+                    parenthesesCounter = 0;
+                }
+                parenthesesCounter++;
+            }
+            if (c == ')')
+            {
+                parenthesesCounter--;
+            }
+            if (parenthesesCounter == 0)
+            {
+                //Close first parentheses
+                count = i+1;
+                break;
+            }
+            if (parenthesesCounter == -1)
+            {
+                if (c == separatorChar)
+                {
+                    count = i;
+                    break;
+                }
+            }
+        }
+        if (count == -1)
+        {
+            return expression.substring(startIndex);
+        }
+        else
+        {
+            return expression.substring(startIndex, count);
+        }
+    }
+    
+    public String[] splitExpressions(String expressions)
+    {
+        // split expressions by blank or comma (and ignore blank and commas inside brackets)
+        String[] splittedExpressions = split(expressions, EXPRESSION_SEPARATOR_CHARS);
+        return splittedExpressions;
+    }
+
+    private static String[] split(String value, char... separators)
+    {
+        if (value == null)
+        {
+            return null;
+        }
+
+        List<String> tokens = new ArrayList<String>();
+        StringBuilder buffer = new StringBuilder();
+
+        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()]);
+    }
+    
+    public List<UIComponent> findComponentFromExpression(SearchExpressionContext searchExpressionContext, 
+            UIComponent source, String topExpression)
+    {
+        // Command pattern to apply the keyword or command to the base and then invoke the callback
+        FacesContext facesContext = searchExpressionContext.getFacesContext();
+        UIComponent currentBase = source;
+        List<UIComponent> responseList = null;
+
+        searchExpressionContext.getExpressionHints().add(SearchExpressionHint.RESOLVE_COMPONENT_LIST);
+
+        //Step 1: find base        
+        //  Case ':' (root)
+        char separatorChar = facesContext.getNamingContainerSeparatorChar();
+        if (topExpression.charAt(0) == separatorChar)
+        {
+            UIComponent findBase;
+            findBase = SearchComponentUtils.getRootComponent(currentBase);
+            return facesContext.getApplication().getSearchExpressionHandler().findComponentFromExpression(
+                    searchExpressionContext, findBase, topExpression.substring(1));
+        }
+
+        //Step 2: Once you have a base where you can start, apply an expression 
+        if (topExpression.charAt(0) == KEYWORD_PREFIX.charAt(0))
+        {
+            // A keyword means apply a command over the current source using an expression and the result must be
+            // feedback into the algorithm.
+            final UIComponent base = currentBase;
+
+            String command = extractKeyword(topExpression, 1, separatorChar);
+            final String remaining = 
+                    command.length()+1 < topExpression.length() ? 
+                        topExpression.substring(1+command.length()+1) : null;
+            
+            final SearchExpressionHandler currentInstance = 
+                    facesContext.getApplication().getSearchExpressionHandler();
+
+            // If the keyword is @child, @composite, @form, @namingcontainer, @next, @none, @parent, @previous,
+            // @root, @this ,  all commands change the source to be applied the action
+            if (remaining != null)
+            {
+                List<UIComponent> resp = this.applyKeyword(searchExpressionContext, base, command);
+                if (resp != null)
+                {
+                    for (UIComponent target : resp)
+                    {
+                        List<UIComponent> list = currentInstance.findComponentFromExpression(
+                                searchExpressionContext, target, remaining);
+                        if (list != null)
+                        {
+                            if (responseList == null)
+                            {
+                                responseList = list;
+                            }
+                            else
+                            {
+                                responseList.addAll(list);
+                            }
+                        }
+                    }
+                }
+            }
+            else
+            {
+                List<UIComponent> resp = this.applyKeyword(searchExpressionContext, base, command);
+                if (resp != null)
+                {
+                    if (responseList == null)
+                    {
+                        responseList = resp;
+                    }
+                    else
+                    {
+                        responseList.addAll(resp);
+                    }
+                }
+            }
+        }
+        else
+        {
+
+            //Split expression into tokens and apply loop
+            String nextExpression = null;
+            String expression;
+            if (topExpression.indexOf(":@") > 0)
+            {
+                int idx = topExpression.indexOf(":@");
+                nextExpression = topExpression.substring(idx+1);
+                expression = topExpression.substring(0,idx);
+            }
+            else
+            {
+                expression = topExpression;
+            }
+
+            // Use findComponent(...) passing the expression provided
+            UIComponent target = currentBase.findComponent(expression);
+            if (target == null)
+            {
+                // If no component is found ...
+                // First try to find the base component.
+
+                // Extract the base id from the expression string
+                int idx = expression.indexOf(separatorChar);
+                String base = idx > 0 ? expression.substring(0, idx) : expression;
+
+                // From the context component clientId, check if the base is part of the clientId
+                String contextClientId = currentBase.getClientId(facesContext);
+                int startCommon = contextClientId.lastIndexOf(base+facesContext.getNamingContainerSeparatorChar());
+                if (startCommon >= 0 
+                    && (startCommon == 0 || contextClientId.charAt(startCommon-1) == separatorChar )
+                    && (startCommon+base.length() <= contextClientId.length()-1 || 
+                        contextClientId.charAt(startCommon+base.length()+1) == separatorChar )) 
+                {
+                    // If there is a match, try to find a the first parent component whose id is equals to
+                    // the base id
+                    UIComponent parent = currentBase;
+                    while (parent != null )
+                    {
+                        if (base.equals(parent.getId()) && parent instanceof NamingContainer)
+                        {
+                            break;
+                        }
+                        else
+                        {
+                            parent = parent.getParent();
+                        }
+                    }
+
+                    // if a base component is found ...
+                    if (parent != null)
+                    {
+                        target = parent.findComponent(expression);
+                        /*
+                        if (target == null)
+                        {
+                            contextClientId = parent.getClientId(facesContext);
+                            // If no component is found,
+                            String targetClientId = contextClientId.substring(0, startCommon+base.length()) + 
+                                    expression.substring(base.length());
+
+                            final SearchExpressionHandler currentHandler = 
+                                    facesContext.getApplication().getSearchExpressionHandler();
+
+                            if (nextExpression != null)
+                            {
+                                final String childExpression = nextExpression;
+
+                                parent.invokeOnComponent(facesContext, targetClientId, new ContextCallback(){
+                                    public void invokeContextCallback(FacesContext context, UIComponent target)
+                                    {
+                                        currentHandler.invokeOnComponentFromExpression(
+                                                searchExpressionContext, target, childExpression, topCallback);
+                                    }
+                                });
+                            }
+                            else
+                            {
+                                parent.invokeOnComponent(facesContext, targetClientId, topCallback);
+                            }
+                            return;
+                        }*/
+                    }
+                }
+            }
+            if (target != null)
+            {
+                currentBase = target;
+            }
+            if (currentBase != null)
+            {
+                if (responseList == null)
+                {
+                    responseList = new ArrayList<UIComponent>();
+                }
+                responseList.add(currentBase);
+            }
+        }
+        return responseList;
+    }
+
+    public List<UIComponent> applyKeyword(SearchExpressionContext searchExpressionContext, UIComponent last, 
+                             String command)
+    {
+        Set<SearchExpressionHint> hints = searchExpressionContext.getExpressionHints();
+        CollectComponentCallback callback = new CollectComponentCallback();
+        searchExpressionContext.getFacesContext().getApplication().getSearchExpressionHandler().applyKeyword(
+                searchExpressionContext, last, command, callback);
+        return callback.getList();
+    }
+    
+    private static class CollectComponentCallback implements ContextCallback
+    {
+        private List<UIComponent> list = null;
+
+        @Override
+        public void invokeContextCallback(FacesContext context, UIComponent target)
+        {
+            if (getList() == null)
+            {
+                list = new ArrayList<UIComponent>(2);
+            }
+            getList().add(target);
+        }
+
+        /**
+         * @return the list
+         */
+        public List<UIComponent> getList()
+        {
+            return list;
+        }
+        
+    }
+}

Added: myfaces/core/branches/2.3.x/impl/src/main/java/org/apache/myfaces/component/search/ThisSearchExpressionResolver.java
URL: http://svn.apache.org/viewvc/myfaces/core/branches/2.3.x/impl/src/main/java/org/apache/myfaces/component/search/ThisSearchExpressionResolver.java?rev=1770904&view=auto
==============================================================================
--- myfaces/core/branches/2.3.x/impl/src/main/java/org/apache/myfaces/component/search/ThisSearchExpressionResolver.java (added)
+++ myfaces/core/branches/2.3.x/impl/src/main/java/org/apache/myfaces/component/search/ThisSearchExpressionResolver.java Wed Nov 23 01:36:27 2016
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.apache.myfaces.component.search;
+
+import javax.faces.component.UIComponent;
+import javax.faces.component.search.SearchExpressionContext;
+import javax.faces.component.search.SearchKeywordContext;
+import javax.faces.component.search.SearchExpressionResolver;
+
+/**
+ *
+ */
+public class ThisSearchExpressionResolver extends SearchExpressionResolver
+{
+    public static final String THIS_KEYWORD = "this";
+
+    @Override
+    public void resolve(SearchKeywordContext expressionContext, UIComponent last, String command)
+    {
+        if (command != null && command.equalsIgnoreCase(THIS_KEYWORD))
+        {
+            expressionContext.invokeContextCallback(expressionContext.getFacesContext(), last);
+        }
+    }
+
+    @Override
+    public boolean matchKeyword(SearchExpressionContext searchExpressionContext, String keyword)
+    {
+        return THIS_KEYWORD.equalsIgnoreCase(keyword);
+    }
+
+    @Override
+    public boolean isPassthroughKeyword(SearchExpressionContext searchExpressionContext, String keyword)
+    {
+        return false;
+    }
+
+    @Override
+    public boolean isLeafKeyword(SearchExpressionContext searchExpressionContext, String keyword)
+    {
+        return false;
+    }
+}

Modified: myfaces/core/branches/2.3.x/impl/src/main/java/org/apache/myfaces/config/DefaultFacesConfigurationProvider.java
URL: http://svn.apache.org/viewvc/myfaces/core/branches/2.3.x/impl/src/main/java/org/apache/myfaces/config/DefaultFacesConfigurationProvider.java?rev=1770904&r1=1770903&r2=1770904&view=diff
==============================================================================
--- myfaces/core/branches/2.3.x/impl/src/main/java/org/apache/myfaces/config/DefaultFacesConfigurationProvider.java (original)
+++ myfaces/core/branches/2.3.x/impl/src/main/java/org/apache/myfaces/config/DefaultFacesConfigurationProvider.java Wed Nov 23 01:36:27 2016
@@ -109,6 +109,7 @@ public class DefaultFacesConfigurationPr
         FACTORY_NAMES.add(FactoryFinder.PARTIAL_VIEW_CONTEXT_FACTORY);
         FACTORY_NAMES.add(FactoryFinder.VISIT_CONTEXT_FACTORY);
         FACTORY_NAMES.add(FactoryFinder.VIEW_DECLARATION_LANGUAGE_FACTORY);
+        FACTORY_NAMES.add(FactoryFinder.SEARCH_EXPRESSION_CONTEXT_FACTORY);
     }
     
     /**
@@ -283,6 +284,10 @@ public class DefaultFacesConfigurationPr
                     {
                         factory.addFaceletCacheFactory(className);
                     }
+                    else if(factoryName.equals(FactoryFinder.SEARCH_EXPRESSION_CONTEXT_FACTORY)) 
+                    {
+                        factory.addSearchExpressionContextFactory(className);
+                    }
                     else
                     {
                         throw new IllegalStateException("Unexpected factory name " + factoryName);

Modified: myfaces/core/branches/2.3.x/impl/src/main/java/org/apache/myfaces/config/FacesConfigDispenser.java
URL: http://svn.apache.org/viewvc/myfaces/core/branches/2.3.x/impl/src/main/java/org/apache/myfaces/config/FacesConfigDispenser.java?rev=1770904&r1=1770903&r2=1770904&view=diff
==============================================================================
--- myfaces/core/branches/2.3.x/impl/src/main/java/org/apache/myfaces/config/FacesConfigDispenser.java (original)
+++ myfaces/core/branches/2.3.x/impl/src/main/java/org/apache/myfaces/config/FacesConfigDispenser.java Wed Nov 23 01:36:27 2016
@@ -126,4 +126,11 @@ public abstract class FacesConfigDispens
     {
     }
 
+    /**
+     * @since 2.3
+     * @param factoryClassName 
+     */
+    public void feedSearchExpressionContextFactory(String factoryClassName)
+    {
+    }
 }

Modified: myfaces/core/branches/2.3.x/impl/src/main/java/org/apache/myfaces/config/FacesConfigurator.java
URL: http://svn.apache.org/viewvc/myfaces/core/branches/2.3.x/impl/src/main/java/org/apache/myfaces/config/FacesConfigurator.java?rev=1770904&r1=1770903&r2=1770904&view=diff
==============================================================================
--- myfaces/core/branches/2.3.x/impl/src/main/java/org/apache/myfaces/config/FacesConfigurator.java (original)
+++ myfaces/core/branches/2.3.x/impl/src/main/java/org/apache/myfaces/config/FacesConfigurator.java Wed Nov 23 01:36:27 2016
@@ -136,6 +136,7 @@ import org.apache.myfaces.shared.util.We
 import org.apache.myfaces.shared_impl.util.serial.DefaultSerialFactory;
 import org.apache.myfaces.shared_impl.util.serial.SerialFactory;
 import org.apache.myfaces.cdi.dependent.BeanEntry;
+import org.apache.myfaces.component.search.SearchExpressionContextFactoryImpl;
 import org.apache.myfaces.config.element.ViewPoolMapping;
 import org.apache.myfaces.config.element.facelets.FaceletTagLibrary;
 import org.apache.myfaces.lifecycle.LifecycleImpl;
@@ -189,6 +190,8 @@ public class FacesConfigurator
     private static final String DEFAULT_FLASH_FACTORY = ServletFlashFactoryImpl.class.getName();
     private static final String DEFAULT_CLIENT_WINDOW_FACTORY = ClientWindowFactoryImpl.class.getName();
     private static final String DEFAULT_FLOW_FACTORY = FlowHandlerFactoryImpl.class.getName();
+    private static final String DEFAULT_SEARCH_EXPRESSION_CONTEXT_FACTORY = 
+            SearchExpressionContextFactoryImpl.class.getName();
     private static final String DEFAULT_FACES_CONFIG = "/WEB-INF/faces-config.xml";
 
     private static final String INJECTED_BEAN_STORAGE_KEY = "org.apache.myfaces.spi.BEAN_ENTRY_STORAGE";
@@ -676,6 +679,9 @@ public class FacesConfigurator
                 DEFAULT_CLIENT_WINDOW_FACTORY);
         setFactories(FactoryFinder.FLOW_HANDLER_FACTORY, dispenser.getFlowHandlerFactoryIterator(),
                 DEFAULT_FLOW_FACTORY);
+        setFactories(FactoryFinder.SEARCH_EXPRESSION_CONTEXT_FACTORY, 
+                dispenser.getSearchExpressionContextFactoryIterator(),
+                DEFAULT_SEARCH_EXPRESSION_CONTEXT_FACTORY);
     }
 
     private void setFactories(String factoryName, Collection<String> factories, String defaultFactory)

Modified: myfaces/core/branches/2.3.x/impl/src/main/java/org/apache/myfaces/config/RuntimeConfig.java
URL: http://svn.apache.org/viewvc/myfaces/core/branches/2.3.x/impl/src/main/java/org/apache/myfaces/config/RuntimeConfig.java?rev=1770904&r1=1770903&r2=1770904&view=diff
==============================================================================
--- myfaces/core/branches/2.3.x/impl/src/main/java/org/apache/myfaces/config/RuntimeConfig.java (original)
+++ myfaces/core/branches/2.3.x/impl/src/main/java/org/apache/myfaces/config/RuntimeConfig.java Wed Nov 23 01:36:27 2016
@@ -33,6 +33,7 @@ import java.util.logging.Logger;
 
 import javax.el.ELResolver;
 import javax.el.ExpressionFactory;
+import javax.faces.component.search.SearchExpressionResolver;
 import javax.faces.context.ExternalContext;
 import javax.faces.el.PropertyResolver;
 import javax.faces.el.VariableResolver;
@@ -121,6 +122,8 @@ public class RuntimeConfig
     private Map<String, Integer> _idByNamespace = new HashMap<String, Integer>();
     
     private List<ViewPoolMapping> _viewPoolMappings = new ArrayList<ViewPoolMapping>();
+    
+    private List<SearchExpressionResolver> _searchExpressionResolvers = new ArrayList<SearchExpressionResolver>();
 
     public static RuntimeConfig getCurrentInstance(ExternalContext externalContext)
     {
@@ -584,4 +587,18 @@ public class RuntimeConfig
     {
         _viewPoolMappings.add(mapping);
     }
+    
+    public void addApplicationSearchExpressionResolver(SearchExpressionResolver resolver)
+    {
+        if (_searchExpressionResolvers == null)
+        {
+            _searchExpressionResolvers = new ArrayList<SearchExpressionResolver>();
+        }
+        _searchExpressionResolvers.add(resolver);
+    }
+
+    public List<SearchExpressionResolver> getApplicationSearchExpressionResolvers()
+    {
+        return _searchExpressionResolvers;
+    }
 }

Modified: myfaces/core/branches/2.3.x/impl/src/main/java/org/apache/myfaces/config/element/FacesConfigData.java
URL: http://svn.apache.org/viewvc/myfaces/core/branches/2.3.x/impl/src/main/java/org/apache/myfaces/config/element/FacesConfigData.java?rev=1770904&r1=1770903&r2=1770904&view=diff
==============================================================================
--- myfaces/core/branches/2.3.x/impl/src/main/java/org/apache/myfaces/config/element/FacesConfigData.java (original)
+++ myfaces/core/branches/2.3.x/impl/src/main/java/org/apache/myfaces/config/element/FacesConfigData.java Wed Nov 23 01:36:27 2016
@@ -100,6 +100,15 @@ public abstract class FacesConfigData im
     {
         return Collections.emptyList();
     }
+    
+    /**
+     * @since 2.3
+     * @return 
+     */
+    public Collection<String> getSearchExpressionContextFactoryIterator()
+    {
+        return Collections.emptyList();
+    }    
 
     /** @return Iterator over ActionListener class names (in reverse order!) */
     public abstract Collection<String> getActionListenerIterator();

Modified: myfaces/core/branches/2.3.x/impl/src/main/java/org/apache/myfaces/config/element/Factory.java
URL: http://svn.apache.org/viewvc/myfaces/core/branches/2.3.x/impl/src/main/java/org/apache/myfaces/config/element/Factory.java?rev=1770904&r1=1770903&r2=1770904&view=diff
==============================================================================
--- myfaces/core/branches/2.3.x/impl/src/main/java/org/apache/myfaces/config/element/Factory.java (original)
+++ myfaces/core/branches/2.3.x/impl/src/main/java/org/apache/myfaces/config/element/Factory.java Wed Nov 23 01:36:27 2016
@@ -83,4 +83,13 @@ public abstract class Factory implements
     {
         return Collections.emptyList();
     }
+    
+    /**
+     * @since 2.3
+     * @return 
+     */
+    public List<String> getSearchExpressionContextFactory()
+    {
+        return Collections.emptyList();
+    }
 }

Modified: myfaces/core/branches/2.3.x/impl/src/main/java/org/apache/myfaces/config/impl/digester/DigesterFacesConfigDispenserImpl.java
URL: http://svn.apache.org/viewvc/myfaces/core/branches/2.3.x/impl/src/main/java/org/apache/myfaces/config/impl/digester/DigesterFacesConfigDispenserImpl.java?rev=1770904&r1=1770903&r2=1770904&view=diff
==============================================================================
--- myfaces/core/branches/2.3.x/impl/src/main/java/org/apache/myfaces/config/impl/digester/DigesterFacesConfigDispenserImpl.java (original)
+++ myfaces/core/branches/2.3.x/impl/src/main/java/org/apache/myfaces/config/impl/digester/DigesterFacesConfigDispenserImpl.java Wed Nov 23 01:36:27 2016
@@ -76,6 +76,7 @@ public class DigesterFacesConfigDispense
     private List<String> flashFactories = new ArrayList<String>();
     private List<String> clientWindowFactories = new ArrayList<String>();
     private List<String> flowHandlerFactories = new ArrayList<String>();
+    private List<String> searchExpressionContextFactories = new ArrayList<String>();
     
     private String defaultRenderKitId;
     private String messageBundle;
@@ -130,7 +131,7 @@ public class DigesterFacesConfigDispense
     private List <String> resourceResolvers = new ArrayList<String>();
     
     private List<ViewPoolMapping> viewPoolMappings = new ArrayList<ViewPoolMapping>();
-    
+        
     // Unmodifiable list/maps to avoid modifications
     private transient List<String> umapplicationFactories;
     private transient List<String> umexceptionHandlerFactories;
@@ -146,6 +147,7 @@ public class DigesterFacesConfigDispense
     private transient List<String> umflashFactories;
     private transient List<String> umclientWindowFactories;
     private transient List<String> umflowHandlerFactories;
+    private transient List<String> umsearchExpressionContextFactories;
     private transient List<Behavior> umbehaviors;
     private transient List<String> umactionListeners;
     private transient List<String> umelResolvers;
@@ -193,6 +195,7 @@ public class DigesterFacesConfigDispense
             flashFactories.addAll(factory.getFlashFactory());
             clientWindowFactories.addAll(factory.getClientWindowFactory());
             flowHandlerFactories.addAll(factory.getFlowHandlerFactory());
+            searchExpressionContextFactories.addAll(factory.getSearchExpressionContextFactory());
         }
 
         components.putAll(config.getComponents());
@@ -1043,4 +1046,21 @@ public class DigesterFacesConfigDispense
         }
         return umviewPoolMappings;
     }
+    
+    @Override
+    public void feedSearchExpressionContextFactory(String factoryClassName)
+    {
+        searchExpressionContextFactories.add(factoryClassName);
+    }
+
+    @Override
+    public Collection<String> getSearchExpressionContextFactoryIterator()
+    {
+        if (umsearchExpressionContextFactories == null)
+        {
+            umsearchExpressionContextFactories = Collections.unmodifiableList(searchExpressionContextFactories);
+        }
+        return umsearchExpressionContextFactories;
+    }
+    
 }

Modified: myfaces/core/branches/2.3.x/impl/src/main/java/org/apache/myfaces/config/impl/digester/DigesterFacesConfigUnmarshallerImpl.java
URL: http://svn.apache.org/viewvc/myfaces/core/branches/2.3.x/impl/src/main/java/org/apache/myfaces/config/impl/digester/DigesterFacesConfigUnmarshallerImpl.java?rev=1770904&r1=1770903&r2=1770904&view=diff
==============================================================================
--- myfaces/core/branches/2.3.x/impl/src/main/java/org/apache/myfaces/config/impl/digester/DigesterFacesConfigUnmarshallerImpl.java (original)
+++ myfaces/core/branches/2.3.x/impl/src/main/java/org/apache/myfaces/config/impl/digester/DigesterFacesConfigUnmarshallerImpl.java Wed Nov 23 01:36:27 2016
@@ -125,6 +125,8 @@ public class DigesterFacesConfigUnmarsha
         digester.addCallMethod("faces-config/factory/partial-view-context-factory", "addPartialViewContextFactory", 0);
         digester.addCallMethod("faces-config/factory/tag-handler-delegate-factory", "addTagHandlerDelegateFactory", 0);
         digester.addCallMethod("faces-config/factory/visit-context-factory", "addVisitContextFactory", 0);
+        digester.addCallMethod("faces-config/factory/search-expression-context-factory", 
+                "addSearchExpressionContextFactory", 0);
         // 2.0 specific end
         
         digester.addObjectCreate("faces-config/application/resource-library-contracts/contract-mapping", 

Modified: myfaces/core/branches/2.3.x/impl/src/main/java/org/apache/myfaces/config/impl/digester/elements/FactoryImpl.java
URL: http://svn.apache.org/viewvc/myfaces/core/branches/2.3.x/impl/src/main/java/org/apache/myfaces/config/impl/digester/elements/FactoryImpl.java?rev=1770904&r1=1770903&r2=1770904&view=diff
==============================================================================
--- myfaces/core/branches/2.3.x/impl/src/main/java/org/apache/myfaces/config/impl/digester/elements/FactoryImpl.java (original)
+++ myfaces/core/branches/2.3.x/impl/src/main/java/org/apache/myfaces/config/impl/digester/elements/FactoryImpl.java Wed Nov 23 01:36:27 2016
@@ -41,6 +41,7 @@ public class FactoryImpl extends org.apa
     private List<String> flowHandlerFactories = new ArrayList<String>();
     private List<String> flashFactories = new ArrayList<String>();
     private List<String> clientWindowFactories = new ArrayList<String>();
+    private List<String> searchExpressionContextFactories = new ArrayList<String>();
 
     public void addApplicationFactory(String factory)
     {
@@ -185,4 +186,16 @@ public class FactoryImpl extends org.apa
     {
         return clientWindowFactories;
     }
+    
+    public void addSearchExpressionContextFactory(String factory)
+    {
+        searchExpressionContextFactories.add(factory);
+    }
+    
+    @Override
+    public List<String> getSearchExpressionContextFactory()
+    {
+        return clientWindowFactories;
+    }
+
 }

Modified: myfaces/core/branches/2.3.x/impl/src/main/java/org/apache/myfaces/renderkit/html/HtmlLabelRenderer.java
URL: http://svn.apache.org/viewvc/myfaces/core/branches/2.3.x/impl/src/main/java/org/apache/myfaces/renderkit/html/HtmlLabelRenderer.java?rev=1770904&r1=1770903&r2=1770904&view=diff
==============================================================================
--- myfaces/core/branches/2.3.x/impl/src/main/java/org/apache/myfaces/renderkit/html/HtmlLabelRenderer.java (original)
+++ myfaces/core/branches/2.3.x/impl/src/main/java/org/apache/myfaces/renderkit/html/HtmlLabelRenderer.java Wed Nov 23 01:36:27 2016
@@ -29,6 +29,7 @@ import javax.faces.component.ValueHolder
 import javax.faces.component.behavior.ClientBehavior;
 import javax.faces.component.behavior.ClientBehaviorHolder;
 import javax.faces.component.html.HtmlOutputLabel;
+import javax.faces.component.search.SearchExpressionContext;
 import javax.faces.context.FacesContext;
 import javax.faces.context.ResponseWriter;
 
@@ -240,7 +241,9 @@ public class HtmlLabelRenderer extends H
 
     protected String getClientId(FacesContext facesContext, UIComponent uiComponent, String forAttr)
     {
-        return RendererUtils.getClientId(facesContext, uiComponent, forAttr);
+        //return RendererUtils.getClientId(facesContext, uiComponent, forAttr);
+        return facesContext.getApplication().getSearchExpressionHandler().resolveClientId(
+                SearchExpressionContext.createSearchExpressionContext(facesContext, uiComponent), forAttr);
     }
 
     @Override

Modified: myfaces/core/branches/2.3.x/impl/src/main/java/org/apache/myfaces/spi/ServiceProviderFinder.java
URL: http://svn.apache.org/viewvc/myfaces/core/branches/2.3.x/impl/src/main/java/org/apache/myfaces/spi/ServiceProviderFinder.java?rev=1770904&r1=1770903&r2=1770904&view=diff
==============================================================================
--- myfaces/core/branches/2.3.x/impl/src/main/java/org/apache/myfaces/spi/ServiceProviderFinder.java (original)
+++ myfaces/core/branches/2.3.x/impl/src/main/java/org/apache/myfaces/spi/ServiceProviderFinder.java Wed Nov 23 01:36:27 2016
@@ -53,6 +53,7 @@ public abstract class ServiceProviderFin
         FactoryFinder.TAG_HANDLER_DELEGATE_FACTORY,
         FactoryFinder.VIEW_DECLARATION_LANGUAGE_FACTORY,
         FactoryFinder.VISIT_CONTEXT_FACTORY,
+        FactoryFinder.SEARCH_EXPRESSION_CONTEXT_FACTORY,
         "org.apache.myfaces.spi.AnnotationProvider",
         "org.apache.myfaces.spi.AnnotationProviderFactory",
         "org.apache.myfaces.spi.FaceletConfigResourceProvider",

Modified: myfaces/core/branches/2.3.x/impl/src/main/java/org/apache/myfaces/view/facelets/compiler/UILeaf.java
URL: http://svn.apache.org/viewvc/myfaces/core/branches/2.3.x/impl/src/main/java/org/apache/myfaces/view/facelets/compiler/UILeaf.java?rev=1770904&r1=1770903&r2=1770904&view=diff
==============================================================================
--- myfaces/core/branches/2.3.x/impl/src/main/java/org/apache/myfaces/view/facelets/compiler/UILeaf.java (original)
+++ myfaces/core/branches/2.3.x/impl/src/main/java/org/apache/myfaces/view/facelets/compiler/UILeaf.java Wed Nov 23 01:36:27 2016
@@ -33,6 +33,7 @@ import javax.faces.component.ContextCall
 import javax.faces.component.UIComponent;
 import javax.faces.component.UIViewRoot;
 import javax.faces.component.UniqueIdVendor;
+import javax.faces.component.search.Markup;
 import javax.faces.component.visit.VisitCallback;
 import javax.faces.component.visit.VisitContext;
 import javax.faces.context.FacesContext;
@@ -47,7 +48,7 @@ import javax.faces.view.Location;
 import org.apache.commons.collections.iterators.EmptyIterator;
 import org.apache.myfaces.view.facelets.tag.jsf.ComponentSupport;
 
-class UILeaf extends UIComponent implements Map<String, Object>
+class UILeaf extends UIComponent implements Markup, Map<String, Object>
 {
     //-------------- START TAKEN FROM UIComponentBase ----------------
     private static final String _STRING_BUILDER_KEY

Modified: myfaces/core/branches/2.3.x/impl/src/main/java/org/apache/myfaces/view/facelets/tag/composite/AttachedObjectTargetImpl.java
URL: http://svn.apache.org/viewvc/myfaces/core/branches/2.3.x/impl/src/main/java/org/apache/myfaces/view/facelets/tag/composite/AttachedObjectTargetImpl.java?rev=1770904&r1=1770903&r2=1770904&view=diff
==============================================================================
--- myfaces/core/branches/2.3.x/impl/src/main/java/org/apache/myfaces/view/facelets/tag/composite/AttachedObjectTargetImpl.java (original)
+++ myfaces/core/branches/2.3.x/impl/src/main/java/org/apache/myfaces/view/facelets/tag/composite/AttachedObjectTargetImpl.java Wed Nov 23 01:36:27 2016
@@ -25,6 +25,7 @@ import java.util.List;
 
 import javax.el.ValueExpression;
 import javax.faces.component.UIComponent;
+import javax.faces.component.search.SearchExpressionContext;
 import javax.faces.context.FacesContext;
 import javax.faces.view.AttachedObjectTarget;
 
@@ -68,28 +69,35 @@ public class AttachedObjectTargetImpl im
         if (targetsArray.length > 0)
         {
             List<UIComponent> targetsList = new ArrayList<UIComponent>(targetsArray.length);
-            final char separatorChar = facesContext.getNamingContainerSeparatorChar();
+            //final char separatorChar = facesContext.getNamingContainerSeparatorChar();
             UIComponent facetBase = topLevelComponent.getFacet(UIComponent.COMPOSITE_FACET_NAME);
             for (String target : targetsArray)
             {
-                //UIComponent innerComponent = topLevelComponent.findComponent(
-                //        topLevelComponent.getId() + UINamingContainer.getSeparatorChar(facesContext) + target);
-                int separator = target.indexOf(separatorChar);
-                UIComponent innerComponent = null;
-                if (separator == -1)
-                {
-                    innerComponent = ComponentSupport.findComponentChildOrFacetFrom(
-                            facetBase, target, null);
-                }
-                else
-                {
-                    innerComponent = ComponentSupport.findComponentChildOrFacetFrom(
-                            facetBase, target.substring(0,separator), target);
-                }
+                //int separator = target.indexOf(separatorChar);
+                //UIComponent innerComponent = null;
+                List<UIComponent> resultList;
+                //if (separator == -1)
+                //{
+                    //innerComponent = ComponentSupport.findComponentChildOrFacetFrom(
+                    //        facetBase, target, null);
+                    resultList = facesContext.getApplication().getSearchExpressionHandler().
+                            findComponentFromExpression(SearchExpressionContext.createSearchExpressionContext(
+                                    facesContext, facetBase), facetBase, target);
+                //}
+                //else
+                //{
+                    //innerComponent = ComponentSupport.findComponentChildOrFacetFrom(
+                    //        facetBase, target.substring(0,separator), target);
+                //    resultList = facesContext.getApplication().getSearchExpressionHandler().
+                //            findComponentFromExpression(SearchExpressionContext.createSearchExpressionContext(
+                //                    facesContext, facetBase), facetBase, target.substring(0,separator));
+                //}
                 
-                if (innerComponent != null)
+                //if (innerComponent != null)
+                if (resultList != null)
                 {
-                    targetsList.add(innerComponent);
+                    targetsList.addAll(resultList);
+                    //targetsList.add(innerComponent);
                 }
             }
             return targetsList;
@@ -103,8 +111,6 @@ public class AttachedObjectTargetImpl im
             String name = getName();
             if (name != null)
             {
-                //UIComponent innerComponent = topLevelComponent.findComponent(
-                //        topLevelComponent.getId() + UINamingContainer.getSeparatorChar(facesContext) + getName());
                 UIComponent innerComponent = ComponentSupport.findComponentChildOrFacetFrom(
                         topLevelComponent.getFacet(UIComponent.COMPOSITE_FACET_NAME),
                         name, null);

Modified: myfaces/core/branches/2.3.x/impl/src/test/java/org/apache/myfaces/application/ApplicationImplJsfTest.java
URL: http://svn.apache.org/viewvc/myfaces/core/branches/2.3.x/impl/src/test/java/org/apache/myfaces/application/ApplicationImplJsfTest.java?rev=1770904&r1=1770903&r2=1770904&view=diff
==============================================================================
--- myfaces/core/branches/2.3.x/impl/src/test/java/org/apache/myfaces/application/ApplicationImplJsfTest.java (original)
+++ myfaces/core/branches/2.3.x/impl/src/test/java/org/apache/myfaces/application/ApplicationImplJsfTest.java Wed Nov 23 01:36:27 2016
@@ -74,7 +74,7 @@ public class ApplicationImplJsfTest exte
      * 
      * @author Jakob Korherr
      */
-    private class TestApplicationWrapper extends ApplicationWrapper
+    public static class TestApplicationWrapper extends ApplicationWrapper
     {
         
         private ApplicationImpl _applicationImpl;

Added: myfaces/core/branches/2.3.x/impl/src/test/java/org/apache/myfaces/component/search/SearchBean.java
URL: http://svn.apache.org/viewvc/myfaces/core/branches/2.3.x/impl/src/test/java/org/apache/myfaces/component/search/SearchBean.java?rev=1770904&view=auto
==============================================================================
--- myfaces/core/branches/2.3.x/impl/src/test/java/org/apache/myfaces/component/search/SearchBean.java (added)
+++ myfaces/core/branches/2.3.x/impl/src/test/java/org/apache/myfaces/component/search/SearchBean.java Wed Nov 23 01:36:27 2016
@@ -0,0 +1,124 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.apache.myfaces.component.search;
+
+import java.util.ArrayList;
+import java.util.List;
+import javax.annotation.PostConstruct;
+import javax.faces.bean.ManagedBean;
+import javax.faces.bean.RequestScoped;
+
+/**
+ *
+ * @author lu4242
+ */
+@ManagedBean(name = "searchBean")
+@RequestScoped
+public class SearchBean
+{
+    
+    private List<RowData> model;
+    
+    public SearchBean()
+    {
+        
+    }
+    
+    @PostConstruct
+    public void init()
+    {
+        setModel(new ArrayList<RowData>());
+        getModel().add(new RowData("text1","style1"));
+        getModel().add(new RowData("text2","style2"));
+        getModel().add(new RowData("text3","style3"));
+        getModel().add(new RowData("text4","style4"));        
+    }
+
+    /**
+     * @return the model
+     */
+    public List<RowData> getModel()
+    {
+        return model;
+    }
+
+    /**
+     * @param model the model to set
+     */
+    public void setModel(List<RowData> model)
+    {
+        this.model = model;
+    }
+    
+    public static class RowData
+    {
+        private String text;
+        
+        private List<String> nested;
+
+        public RowData(String text, String style)
+        {
+           super();
+            this.text = text;
+            this.style = style;
+            this.nested = new ArrayList<String>();
+            this.nested.add("A");
+            this.nested.add("B");
+        }
+
+        private String style;
+        
+        public String getText()
+        {
+            return text;
+        }
+
+        public void setText(String text)
+        {
+            this.text = text;
+        }
+
+        public String getStyle()
+        {
+            return style;
+        }
+
+        public void setStyle(String style)
+        {
+            this.style = style;
+        }
+
+        /**
+         * @return the nested
+         */
+        public List<String> getNested()
+        {
+            return nested;
+        }
+
+        /**
+         * @param nested the nested to set
+         */
+        public void setNested(List<String> nested)
+        {
+            this.nested = nested;
+        }
+    }
+}