View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.myfaces.view.facelets;
20  
21  import java.beans.BeanDescriptor;
22  import java.beans.BeanInfo;
23  import java.beans.PropertyDescriptor;
24  import java.io.FileNotFoundException;
25  import java.io.IOException;
26  import java.io.Writer;
27  import java.util.ArrayList;
28  import java.util.Collections;
29  import java.util.EnumSet;
30  import java.util.HashMap;
31  import java.util.HashSet;
32  import java.util.List;
33  import java.util.Map;
34  import java.util.Set;
35  import java.util.logging.Level;
36  import java.util.logging.Logger;
37  
38  import javax.el.ELContext;
39  import javax.el.ELException;
40  import javax.el.MethodExpression;
41  import javax.el.ValueExpression;
42  import javax.el.VariableMapper;
43  import javax.faces.FacesException;
44  import javax.faces.FacesWrapper;
45  import javax.faces.FactoryFinder;
46  import javax.faces.application.Application;
47  import javax.faces.application.ProjectStage;
48  import javax.faces.application.Resource;
49  import javax.faces.application.StateManager;
50  import javax.faces.application.ViewHandler;
51  import javax.faces.component.ActionSource2;
52  import javax.faces.component.EditableValueHolder;
53  import javax.faces.component.UIComponent;
54  import javax.faces.component.UINamingContainer;
55  import javax.faces.component.UIPanel;
56  import javax.faces.component.UIViewRoot;
57  import javax.faces.component.visit.VisitContext;
58  import javax.faces.component.visit.VisitHint;
59  import javax.faces.context.ExternalContext;
60  import javax.faces.context.FacesContext;
61  import javax.faces.context.ResponseWriter;
62  import javax.faces.event.ActionEvent;
63  import javax.faces.event.ActionListener;
64  import javax.faces.event.MethodExpressionActionListener;
65  import javax.faces.event.MethodExpressionValueChangeListener;
66  import javax.faces.event.PhaseId;
67  import javax.faces.event.PostAddToViewEvent;
68  import javax.faces.event.PostRestoreStateEvent;
69  import javax.faces.event.ValueChangeEvent;
70  import javax.faces.event.ValueChangeListener;
71  import javax.faces.render.RenderKit;
72  import javax.faces.render.RenderKitFactory;
73  import javax.faces.render.ResponseStateManager;
74  import javax.faces.validator.MethodExpressionValidator;
75  import javax.faces.validator.Validator;
76  import javax.faces.view.ActionSource2AttachedObjectHandler;
77  import javax.faces.view.ActionSource2AttachedObjectTarget;
78  import javax.faces.view.AttachedObjectHandler;
79  import javax.faces.view.AttachedObjectTarget;
80  import javax.faces.view.BehaviorHolderAttachedObjectHandler;
81  import javax.faces.view.BehaviorHolderAttachedObjectTarget;
82  import javax.faces.view.EditableValueHolderAttachedObjectHandler;
83  import javax.faces.view.EditableValueHolderAttachedObjectTarget;
84  import javax.faces.view.StateManagementStrategy;
85  import javax.faces.view.ValueHolderAttachedObjectHandler;
86  import javax.faces.view.ValueHolderAttachedObjectTarget;
87  import javax.faces.view.ViewDeclarationLanguage;
88  import javax.faces.view.ViewMetadata;
89  import javax.faces.view.facelets.Facelet;
90  import javax.faces.view.facelets.FaceletContext;
91  import javax.faces.view.facelets.ResourceResolver;
92  import javax.servlet.http.HttpServletResponse;
93  import org.apache.myfaces.application.StateManagerImpl;
94  
95  import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFWebConfigParam;
96  import org.apache.myfaces.config.RuntimeConfig;
97  import org.apache.myfaces.shared.application.DefaultViewHandlerSupport;
98  import org.apache.myfaces.shared.application.ViewHandlerSupport;
99  import org.apache.myfaces.shared.config.MyfacesConfig;
100 import org.apache.myfaces.shared.util.ClassUtils;
101 import org.apache.myfaces.shared.util.StringUtils;
102 import org.apache.myfaces.shared.util.WebConfigParamUtils;
103 import org.apache.myfaces.view.ViewDeclarationLanguageStrategy;
104 import org.apache.myfaces.view.ViewMetadataBase;
105 import org.apache.myfaces.view.facelets.compiler.Compiler;
106 import org.apache.myfaces.view.facelets.compiler.SAXCompiler;
107 import org.apache.myfaces.view.facelets.el.CompositeComponentELUtils;
108 import org.apache.myfaces.view.facelets.el.LocationMethodExpression;
109 import org.apache.myfaces.view.facelets.el.LocationValueExpression;
110 import org.apache.myfaces.view.facelets.el.MethodExpressionMethodExpression;
111 import org.apache.myfaces.view.facelets.el.RedirectMethodExpressionValueExpressionActionListener;
112 import org.apache.myfaces.view.facelets.el.RedirectMethodExpressionValueExpressionValidator;
113 import org.apache.myfaces.view.facelets.el.RedirectMethodExpressionValueExpressionValueChangeListener;
114 import org.apache.myfaces.view.facelets.el.ValueExpressionMethodExpression;
115 import org.apache.myfaces.view.facelets.el.VariableMapperWrapper;
116 import org.apache.myfaces.view.facelets.impl.DefaultFaceletFactory;
117 import org.apache.myfaces.view.facelets.impl.DefaultResourceResolver;
118 import org.apache.myfaces.view.facelets.tag.composite.ClientBehaviorAttachedObjectTarget;
119 import org.apache.myfaces.view.facelets.tag.composite.ClientBehaviorRedirectBehaviorAttachedObjectHandlerWrapper;
120 import org.apache.myfaces.view.facelets.tag.composite.ClientBehaviorRedirectEventComponentWrapper;
121 import org.apache.myfaces.view.facelets.tag.jsf.ComponentSupport;
122 import org.apache.myfaces.view.facelets.tag.jsf.core.AjaxHandler;
123 import org.apache.myfaces.view.facelets.tag.ui.UIDebug;
124 
125 import static org.apache.myfaces.view.facelets.DefaultFaceletsStateManagementStrategy.*;
126 import org.apache.myfaces.view.facelets.compiler.FaceletsCompilerSupport;
127 import org.apache.myfaces.view.facelets.compiler.RefreshDynamicComponentListener;
128 import org.apache.myfaces.view.facelets.impl.SectionUniqueIdCounter;
129 import org.apache.myfaces.view.facelets.pool.RestoreViewFromPoolResult;
130 import org.apache.myfaces.view.facelets.pool.ViewEntry;
131 import org.apache.myfaces.view.facelets.pool.ViewPool;
132 import org.apache.myfaces.view.facelets.pool.ViewStructureMetadata;
133 import org.apache.myfaces.view.facelets.tag.composite.CreateDynamicCompositeComponentListener;
134 import org.apache.myfaces.view.facelets.tag.jsf.PartialMethodExpressionActionListener;
135 import org.apache.myfaces.view.facelets.tag.jsf.PartialMethodExpressionValidator;
136 import org.apache.myfaces.view.facelets.tag.jsf.PartialMethodExpressionValueChangeListener;
137 import org.apache.myfaces.view.facelets.util.FaceletsViewDeclarationLanguageUtils;
138 
139 /**
140  * This class represents the abstraction of Facelets as a ViewDeclarationLanguage.
141  *
142  * @author Simon Lessard (latest modification by $Author: lu4242 $)
143  * @version $Revision: 1634897 $ $Date: 2014-10-28 15:49:14 +0000 (Tue, 28 Oct 2014) $
144  *
145  * @since 2.0
146  */
147 public class FaceletViewDeclarationLanguage extends FaceletViewDeclarationLanguageBase
148 {
149     //private static final Log log = LogFactory.getLog(FaceletViewDeclarationLanguage.class);
150     private static final Logger log = Logger.getLogger(FaceletViewDeclarationLanguage.class.getName());
151 
152     private static final Class<?>[] EMPTY_CLASS_ARRAY = new Class<?>[0];
153 
154     private static final Class<?>[] VALUE_CHANGE_LISTENER_SIGNATURE = new Class[]{ValueChangeEvent.class};
155 
156     private static final Class<?>[] ACTION_LISTENER_SIGNATURE = new Class[]{ActionEvent.class};
157 
158     private static final Class<?>[] VALIDATOR_SIGNATURE
159             = new Class[]{FacesContext.class, UIComponent.class, Object.class};
160 
161     public static final String CHARACTER_ENCODING_KEY = "javax.faces.request.charset";
162 
163     public final static long DEFAULT_REFRESH_PERIOD = 0;
164     public final static long DEFAULT_REFRESH_PERIOD_PRODUCTION = -1;
165 
166     public final static String DEFAULT_CHARACTER_ENCODING = "UTF-8";
167 
168     /**
169      * Define the default buffer size value passed to ExternalContext.setResponseBufferResponse() and in a
170      * servlet environment to HttpServletResponse.setBufferSize().
171      */
172     @JSFWebConfigParam(since = "2.0", deprecated = true, classType = "java.lang.Integer")
173     private final static String PARAM_BUFFER_SIZE_DEPRECATED = "facelets.BUFFER_SIZE";
174 
175     private final static String[] PARAMS_BUFFER_SIZE = {ViewHandler.FACELETS_BUFFER_SIZE_PARAM_NAME,
176         PARAM_BUFFER_SIZE_DEPRECATED};
177 
178     //private final static String PARAM_BUILD_BEFORE_RESTORE = "facelets.BUILD_BEFORE_RESTORE";
179 
180     /**
181      * Constant used by EncodingHandler to indicate the current encoding of the page being built,
182      * and indicate which one is the response encoding on getResponseEncoding(FacesContext, String) method.
183      */
184     public final static String PARAM_ENCODING = "facelets.Encoding";
185 
186     /**
187      * Define the period used to refresh the facelet abstract syntax tree from the view definition file. 
188      *
189      * <p>By default is infinite (no active).</p>
190      */
191     @JSFWebConfigParam(since = "2.0", defaultValue = "-1", deprecated = true)
192     public final static String PARAM_REFRESH_PERIOD_DEPRECATED = "facelets.REFRESH_PERIOD";
193 
194     public final static String[] PARAMS_REFRESH_PERIOD = {ViewHandler.FACELETS_REFRESH_PERIOD_PARAM_NAME,
195         PARAM_REFRESH_PERIOD_DEPRECATED};
196 
197     /**
198      * Class implementing ResourceResolver interface used to locate facelet resources. 
199      */
200     @JSFWebConfigParam(since = "2.0", alias = "facelets.RESOURCE_RESOLVER")
201     public final static String PARAM_RESOURCE_RESOLVER = "javax.faces.FACELETS_RESOURCE_RESOLVER";
202 
203     /**
204      * Class implementing ResourceResolver interface used to locate facelet resources.
205      */
206     @JSFWebConfigParam(since = "2.0", deprecated = true)
207     private final static String PARAM_RESOURCE_RESOLVER_DEPRECATED = "facelets.RESOURCE_RESOLVER";
208 
209     private final static String[] PARAMS_RESOURCE_RESOLVER
210             = {PARAM_RESOURCE_RESOLVER, PARAM_RESOURCE_RESOLVER_DEPRECATED};
211 
212     @JSFWebConfigParam(since = "2.1", defaultValue = "false", expectedValues = "true, false", tags = "performance")
213     private final static String PARAM_MARK_INITIAL_STATE_WHEN_APPLY_BUILD_VIEW
214             = "org.apache.myfaces.MARK_INITIAL_STATE_WHEN_APPLY_BUILD_VIEW";
215 
216     public final static String FILLED_VIEW = "org.apache.myfaces.FILLED_VIEW";
217 
218     //BEGIN CONSTANTS SET ON BUILD VIEW
219 
220     public final static String BUILDING_VIEW_METADATA = "org.apache.myfaces.BUILDING_VIEW_METADATA";
221 
222     public final static String REFRESHING_TRANSIENT_BUILD = "org.apache.myfaces.REFRESHING_TRANSIENT_BUILD";
223 
224     public final static String REFRESH_TRANSIENT_BUILD_ON_PSS = "org.apache.myfaces.REFRESH_TRANSIENT_BUILD_ON_PSS";
225 
226     public final static String USING_PSS_ON_THIS_VIEW = "org.apache.myfaces.USING_PSS_ON_THIS_VIEW";
227 
228     public final static String REMOVING_COMPONENTS_BUILD = "org.apache.myfaces.REMOVING_COMPONENTS_BUILD";
229     //END CONSTANTS SET ON BUILD VIEW
230 
231     /**
232      * Marker to indicate tag handlers the view currently being built is using
233      * partial state saving and it is necessary to call UIComponent.markInitialState
234      * after component instances are populated. 
235      */
236     public final static String MARK_INITIAL_STATE_KEY = "org.apache.myfaces.MARK_INITIAL_STATE";
237     
238     public final static String IS_BUILDING_INITIAL_STATE_KEY_ALIAS
239             = "javax.faces.view.ViewDeclarationLanguage.IS_BUILDING_INITIAL_STATE";
240 
241     public final static String CLEAN_TRANSIENT_BUILD_ON_RESTORE
242             = "org.apache.myfaces.CLEAN_TRANSIENT_BUILD_ON_RESTORE";
243 
244     private final static String STATE_KEY = "<!--@@JSF_FORM_STATE_MARKER@@-->";
245 
246     private final static int STATE_KEY_LEN = STATE_KEY.length();
247     
248     private static final Set<VisitHint> VISIT_HINTS_DYN_REFRESH = Collections.unmodifiableSet( 
249             EnumSet.of(VisitHint.SKIP_ITERATION));
250     
251     /**
252      * Key used to cache component ids for the counter
253      */
254     public final static String CACHED_COMPONENT_IDS = "oam.CACHED_COMPONENT_IDS"; 
255     
256     private static final String ASTERISK = "*";
257 
258     private int _bufferSize;
259 
260     // This param evolve in jsf 2.0 to partial state saving
261     //private boolean _buildBeforeRestore = false;
262 
263     private ViewHandlerSupport _cachedViewHandlerSupport;
264 
265     private String _defaultSuffix;
266 
267     private FaceletFactory _faceletFactory;
268 
269     private StateManagementStrategy _stateMgmtStrategy;
270     
271     private RenderKitFactory _renderKitFactory = null;
272 
273     private boolean _partialStateSaving;
274 
275     private boolean _refreshTransientBuildOnPSS;
276 
277     private boolean _refreshTransientBuildOnPSSAuto;
278 
279     private Set<String> _viewIds;
280 
281     private boolean _markInitialStateWhenApplyBuildView;
282 
283     private final ViewDeclarationLanguageStrategy _strategy;
284 
285     private ResourceResolver _resourceResolver;
286     
287     private Map<String, List<String>> _contractMappings;
288     private List<String> _prefixWildcardKeys;
289     
290     private FaceletsCompilerSupport _faceletsCompilerSupport;
291     
292     private MyfacesConfig _config;
293     
294     private ViewPoolProcessor _viewPoolProcessor;
295 
296     /**
297      *
298      */
299     public FaceletViewDeclarationLanguage(FacesContext context)
300     {
301         _config = MyfacesConfig.getCurrentInstance(context.getExternalContext());
302         initialize(context);
303         _strategy = new FaceletViewDeclarationLanguageStrategy();
304     }
305 
306     public FaceletViewDeclarationLanguage(FacesContext context, ViewDeclarationLanguageStrategy strategy)
307     {
308         _config = MyfacesConfig.getCurrentInstance(context.getExternalContext());
309         initialize(context);
310         _strategy = strategy;
311     }
312 
313 
314     @Override
315     public String getId()
316     {
317         return ViewDeclarationLanguage.FACELETS_VIEW_DECLARATION_LANGUAGE_ID;
318     }
319 
320     @Override
321     public boolean viewExists(FacesContext facesContext, String viewId)
322     {
323         if (_strategy.handles(viewId))
324         {
325             return _resourceResolver.resolveUrl(viewId) != null;
326         }
327         return false;
328     }
329 
330     private RestoreViewFromPoolResult tryRestoreViewFromCache(FacesContext context, UIViewRoot view)
331     {
332         if (_viewPoolProcessor != null)
333         {
334             ViewPool viewPool = _viewPoolProcessor.getViewPool(context, view);
335             if (viewPool != null)
336             {
337                 ViewStructureMetadata metadata = viewPool.retrieveStaticViewStructureMetadata(context, view);
338                 if (metadata != null)
339                 {
340                     ViewEntry entry = viewPool.popStaticOrPartialStructureView(context, view);
341                     if (entry != null)
342                     {
343                         _viewPoolProcessor.cloneAndRestoreView(context, view, entry, metadata);
344                         return entry.getResult();
345                     }
346                 }
347             }
348         }
349         return null;
350     }
351 
352     /**
353      * {@inheritDoc}
354      */
355     @Override
356     public void buildView(FacesContext context, UIViewRoot view) throws IOException
357     {
358         if (isFilledView(context, view))
359         {
360             if (view != null && 
361                 FaceletViewDeclarationLanguageBase.isDynamicComponentRefreshTransientBuildActive(context, view))
362             {
363                 // don't return
364             }
365             else
366             {
367                 return;
368             }
369         }
370 
371         // setup our viewId
372         String previousViewId = view.getViewId();
373         String renderedViewId = getRenderedViewId(context, previousViewId);
374 
375         if (renderedViewId == null)
376         {
377             view.setViewId(renderedViewId);
378         }
379         else if (!renderedViewId.equals(previousViewId))
380         {
381             view.setViewId(renderedViewId);
382         }
383 
384         if (log.isLoggable(Level.FINEST))
385         {
386             log.finest("Building View: " + renderedViewId);
387         }
388 
389         boolean usePartialStateSavingOnThisView = _usePartialStateSavingOnThisView(renderedViewId);
390         boolean refreshTransientBuild = (view.getChildCount() > 0);
391         boolean refreshTransientBuildOnPSS = (usePartialStateSavingOnThisView && _refreshTransientBuildOnPSS);
392         boolean refreshPartialView = false;
393 
394         if (_viewPoolProcessor != null && !refreshTransientBuild)
395         {
396             RestoreViewFromPoolResult result = tryRestoreViewFromCache(context, view);
397             if (result != null)
398             {
399                 // Since all transient stuff has been removed, add listeners that keep
400                 // track of tree updates.
401                 if (RestoreViewFromPoolResult.COMPLETE.equals(result))
402                 {
403                     if (!PhaseId.RESTORE_VIEW.equals(context.getCurrentPhaseId()))
404                     {
405                         ((DefaultFaceletsStateManagementStrategy) 
406                                 getStateManagementStrategy(context, view.getViewId())).
407                                 suscribeListeners(view);
408                     }
409                     // If the result is complete, the view restored here is static. 
410                     // static views can be marked as filled.
411                     if (!refreshTransientBuildOnPSS)
412                     {
413                         // This option will be true on this cases:
414                         // -pss is true and refresh is not active
415                         setFilledView(context, view);
416                     }
417                     //At this point refreshTransientBuild = false && refreshTransientBuildOnPSS is true
418                     else if (_refreshTransientBuildOnPSSAuto &&
419                              !context.getAttributes().containsKey(CLEAN_TRANSIENT_BUILD_ON_RESTORE))
420                     {
421                         setFilledView(context, view);
422                     }
423                     return;
424                 }
425                 else
426                 {
427                     // We need to refresh a partial view.
428                     refreshTransientBuild = true;
429                     refreshPartialView = true;
430                 }
431             }
432         }
433         
434         if (usePartialStateSavingOnThisView)
435         {
436             // Before apply we need to make sure the current view has
437             // a clientId that will be used as a key to save and restore
438             // the current view. Note that getClientId is never called (or used)
439             // from UIViewRoot.
440             if (view.getId() == null)
441             {
442                 view.setId(view.createUniqueId(context, null));
443             }
444 
445             context.getAttributes().put(USING_PSS_ON_THIS_VIEW, Boolean.TRUE);
446             //Add a key to indicate ComponentTagHandlerDelegate to 
447             //call UIComponent.markInitialState after it is populated
448             if (!refreshTransientBuild || refreshPartialView)
449             {
450                 context.getAttributes().put(StateManager.IS_BUILDING_INITIAL_STATE, Boolean.TRUE);
451                 context.getAttributes().put(IS_BUILDING_INITIAL_STATE_KEY_ALIAS, Boolean.TRUE);
452             }
453             if (!refreshTransientBuild && _markInitialStateWhenApplyBuildView)
454             {
455                 context.getAttributes().put(MARK_INITIAL_STATE_KEY, Boolean.TRUE);
456             }
457             if (refreshTransientBuildOnPSS)
458             {
459                 //This value is only set when _refreshTransientBuildOnPSSMode is "auto" or "true" 
460                 context.getAttributes().put(REFRESH_TRANSIENT_BUILD_ON_PSS,
461                                             _refreshTransientBuildOnPSSAuto ? "auto" : "true");
462             }
463         }
464 
465         try
466         {
467             if (refreshTransientBuild)
468             {
469                 context.getAttributes().put(REFRESHING_TRANSIENT_BUILD, Boolean.TRUE);
470 
471                 // In theory, this should be disabled on ComponentTagHandlerDelegate,
472                 // otherwise we could lost PostAddToViewEvent / PreRemoveFromViewEvent
473                 // caused by c:if effect or facelets cleanup algorithm
474                 //context.setProcessingEvents(false);
475             }
476             // populate UIViewRoot
477             _getFacelet(context, renderedViewId).apply(context, view);
478         }
479         finally
480         {
481             if (refreshTransientBuildOnPSS)
482             {
483                 context.getAttributes().remove(REFRESH_TRANSIENT_BUILD_ON_PSS);
484             }
485             if (refreshTransientBuild)
486             {
487                 //context.setProcessingEvents(true);
488                 if (FaceletViewDeclarationLanguageBase.isDynamicComponentRefreshTransientBuildActive(context))
489                 {
490                     VisitContext visitContext = (VisitContext) getVisitContextFactory().
491                         getVisitContext(context, null, VISIT_HINTS_DYN_REFRESH);
492                     view.visitTree(visitContext, new PublishDynamicComponentRefreshTransientBuildCallback());
493                 }
494                 if (!usePartialStateSavingOnThisView || refreshTransientBuildOnPSS)
495                 {
496                     // When the facelet is applied, all components are removed and added from view,
497                     // but the difference resides in the ordering. Since the view is
498                     // being refreshed, if we don't do this manually, some tags like
499                     // cc:insertChildren or cc:insertFacet will not work correctly, because
500                     // we expect PostAddToViewEvent will be propagated from parent to child, and
501                     // facelets refreshing algorithm do the opposite.
502                     //FaceletViewDeclarationLanguage._publishPreRemoveFromViewEvent(context, view);
503                     //FaceletViewDeclarationLanguage._publishPostAddToViewEvent(context, view);
504                     FaceletViewDeclarationLanguage._publishPostBuildComponentTreeOnRestoreViewEvent(context, view);
505                 }
506 
507                 context.getAttributes().remove(REFRESHING_TRANSIENT_BUILD);
508             }
509             else
510             {
511                 // Publish PostAddToView over UIViewRoot, because this is not done automatically.
512                 context.getApplication().publishEvent(context, PostAddToViewEvent.class, UIViewRoot.class, view);
513             }
514         }
515 
516         // set this view as filled
517         if (refreshTransientBuild)
518         {
519             //This option will be true on this cases:
520             //- pss is false, but we are refreshing
521             //- pss is true, and we are refreshing a view already filled
522             setFilledView(context, view);
523         }
524         else if (!refreshTransientBuildOnPSS)
525         {
526             // This option will be true on this cases:
527             // -pss is true and refresh is not active
528             setFilledView(context, view);
529         }
530         //At this point refreshTransientBuild = false && refreshTransientBuildOnPSS is true
531         else if (_refreshTransientBuildOnPSSAuto &&
532                  !context.getAttributes().containsKey(CLEAN_TRANSIENT_BUILD_ON_RESTORE))
533         {
534             setFilledView(context, view);
535         }
536 
537         // Suscribe listeners if we are using partialStateSaving
538         if (usePartialStateSavingOnThisView)
539         {
540             // UIViewRoot.markInitialState() is not called because it does
541             // not have a facelet tag handler class that create it, instead
542             // new instances are created programatically.
543             if (!refreshTransientBuild || refreshPartialView)
544             {
545                 // Save the state
546                 if (_viewPoolProcessor != null &&
547                     _viewPoolProcessor.isViewPoolEnabledForThisView(context, view))
548                 {
549                     _viewPoolProcessor.storeViewStructureMetadata(context, view);
550                 }
551                 if (_markInitialStateWhenApplyBuildView)
552                 {
553                     if (!refreshTransientBuildOnPSS ||
554                         !view.getAttributes().containsKey(COMPONENT_ADDED_AFTER_BUILD_VIEW))
555                     {
556                         view.markInitialState();
557                     }
558 
559                     //Remove the key that indicate we need to call UIComponent.markInitialState
560                     //on the current tree
561                     context.getAttributes().remove(MARK_INITIAL_STATE_KEY);
562                 }
563                 else
564                 {
565                     context.getAttributes().put(MARK_INITIAL_STATE_KEY, Boolean.TRUE);
566                     _markInitialStateOnView(view, refreshTransientBuildOnPSS);
567                     context.getAttributes().remove(MARK_INITIAL_STATE_KEY);
568                 }
569                 context.getAttributes().remove(StateManager.IS_BUILDING_INITIAL_STATE);
570                 context.getAttributes().remove(IS_BUILDING_INITIAL_STATE_KEY_ALIAS);
571             }
572 
573             // We need to suscribe the listeners of changes in the component tree
574             // only the first time here. Later we suscribe this listeners on
575             // DefaultFaceletsStateManagement.restoreView after calling 
576             // _publishPostBuildComponentTreeOnRestoreViewEvent(), to ensure 
577             // relocated components are not retrieved later on getClientIdsRemoved().
578             if (!(refreshTransientBuild && PhaseId.RESTORE_VIEW.equals(context.getCurrentPhaseId())) &&
579                 !view.isTransient())
580             {
581                 ((DefaultFaceletsStateManagementStrategy) getStateManagementStrategy(context, view.getViewId())).
582                         suscribeListeners(view);
583             }
584 
585             context.getAttributes().remove(USING_PSS_ON_THIS_VIEW);
586         }
587 
588         // Remove this var from faces context because this one prevent AjaxHandler
589         // register the standard script library on Post-Redirect-Get pattern or
590         // in the next view
591         context.getAttributes().remove(AjaxHandler.STANDARD_JSF_AJAX_LIBRARY_LOADED);
592     }
593 
594     private void _markInitialStateOnView(final UIViewRoot view, final boolean refreshTransientBuildOnPSS)
595     {
596         if (!refreshTransientBuildOnPSS ||
597                 !view.getAttributes().containsKey(COMPONENT_ADDED_AFTER_BUILD_VIEW))
598         {
599             if (!view.isTransient())
600             {
601                 view.markInitialState();
602             }
603         }
604 
605         int childCount = view.getChildCount();
606         if (childCount > 0)
607         {
608             for (int i = 0; i < childCount; i++)
609             {
610                 UIComponent child = view.getChildren().get(i);
611                 if (!child.isTransient())
612                 {
613                     _markInitialState(child);
614                 }
615             }
616         }
617         if (view.getFacetCount() > 0)
618         {
619             Map<String, UIComponent> facetMap = view.getFacets();
620             for (Map.Entry<String, UIComponent> entry : facetMap.entrySet())
621             {
622                 UIComponent child = entry.getValue();
623                 if (!child.isTransient())
624                 {
625                     _markInitialState(child);
626                 }
627             }
628 
629         }
630     }
631 
632     private void _markInitialState(final UIComponent component)
633     {
634         component.markInitialState();
635 
636         final int childCount = component.getChildCount();
637         if (childCount > 0)
638         {
639             for (int i = 0; i < childCount; i++)
640             {
641                 UIComponent child = component.getChildren().get(i);
642                 if (!child.isTransient())
643                 {
644                     _markInitialState(child);
645                 }
646             }
647         }
648         if (component.getFacetCount() > 0)
649         {
650             Map<String, UIComponent> facetMap = component.getFacets();
651             for (Map.Entry<String, UIComponent> entry : facetMap.entrySet())
652             {
653                 UIComponent child = entry.getValue();
654                 if (!child.isTransient())
655                 {
656                     _markInitialState(child);
657                 }
658             }
659 
660         }
661     }
662 
663     public static void _publishPostBuildComponentTreeOnRestoreViewEvent(FacesContext context, UIComponent component)
664     {
665         context.getApplication().publishEvent(context, PostBuildComponentTreeOnRestoreViewEvent.class,
666                                               component.getClass(), component);
667 
668         if (component.getChildCount() > 0)
669         {
670             // PostAddToViewEvent could cause component relocation
671             // (h:outputScript, h:outputStylesheet, composite:insertChildren, composite:insertFacet)
672             // so we need to check if the component was relocated or not
673             List<UIComponent> children = component.getChildren();
674             UIComponent child = null;
675             UIComponent currentChild = null;
676             int i = 0;
677             while (i < children.size())
678             {
679                 child = children.get(i);
680                 // Iterate over the same index if the component was removed
681                 // This prevents skip components when processing
682                 do
683                 {
684                     _publishPostBuildComponentTreeOnRestoreViewEvent(context, child);
685                     currentChild = child;
686                     child = children.get(i);
687                 }
688                 while ((i < children.size()) && child != currentChild);
689                 i++;
690             }
691         }
692         if (component.getFacetCount() > 0)
693         {
694             for (UIComponent child : component.getFacets().values())
695             {
696                 _publishPostBuildComponentTreeOnRestoreViewEvent(context, child);
697             }
698         }
699     }
700 
701     private boolean isFilledView(FacesContext context, UIViewRoot view)
702     {
703         // The view is only built on restoreView or renderView, but if
704         // we are not using partial state saving, we need to mark the current
705         // view as filled, otherwise it will be filled again on renderView.
706         return context.getAttributes().containsKey(view);
707         // -= Leonardo Uribe =- save this key on view cause render fail, because the view
708         // is built before render view to "restore" the transient components that has
709         // facelet markup (facelets UIInstructions ...) This effect is only notice when
710         // partial state saving is not used. 
711         //return view.getAttributes().containsKey(FILLED_VIEW);
712     }
713 
714     private void setFilledView(FacesContext context, UIViewRoot view)
715     {
716         context.getAttributes().put(view, Boolean.TRUE);
717         // -= Leonardo Uribe =- save this key on view cause render fail, because the view
718         // is built before render view to "restore" the transient components that has
719         // facelet markup (facelets UIInstructions ...) This effect is only notice when
720         // partial state saving is not used. 
721         // view.getAttributes().put(FILLED_VIEW, Boolean.TRUE);
722     }
723 
724     /**
725      * retargetMethodExpressions(FacesContext, UIComponent) has some clues about the behavior of this method
726      *
727      * {@inheritDoc}
728      */
729     @Override
730     public BeanInfo getComponentMetadata(FacesContext context, Resource componentResource)
731     {
732         BeanInfo beanInfo = null;
733 
734         checkNull(context, "context");
735 
736         try
737         {
738             Facelet compositeComponentFacelet;
739             FaceletFactory.setInstance(_faceletFactory);
740             try
741             {
742                 compositeComponentFacelet
743                         = _faceletFactory.getCompositeComponentMetadataFacelet(componentResource.getURL());
744             }
745             finally
746             {
747                 FaceletFactory.setInstance(null);
748             }
749             //context.getAttributes().put(BUILDING_COMPOSITE_COMPONENT_METADATA, Boolean.TRUE);
750 
751             // Create a temporal tree where all components will be put, but we are only
752             // interested in metadata.
753             UINamingContainer compositeComponentBase
754                     = (UINamingContainer) context.getApplication().createComponent(
755                     context, UINamingContainer.COMPONENT_TYPE, null);
756 
757             // Fill the component resource key, because this information should be available
758             // on metadata to recognize which is the component used as composite component base.
759             // Since this method is called from Application.createComponent(FacesContext,Resource),
760             // and in that specific method this key is updated, this is the best option we
761             // have for recognize it (also this key is used by UIComponent.isCompositeComponent)
762             compositeComponentBase.getAttributes().put(Resource.COMPONENT_RESOURCE_KEY, componentResource);
763 
764             // According to UserTagHandler, in this point we need to wrap the facelet
765             // VariableMapper, so local changes are applied on "page context", but
766             // data is retrieved from full context
767             FaceletContext faceletContext = (FaceletContext) context.
768                     getAttributes().get(FaceletContext.FACELET_CONTEXT_KEY);
769             VariableMapper orig = faceletContext.getVariableMapper();
770             try
771             {
772                 faceletContext.setVariableMapper(new VariableMapperWrapper(orig));
773 
774                 compositeComponentBase.pushComponentToEL(context, compositeComponentBase);
775 
776                 compositeComponentFacelet.apply(context, compositeComponentBase);
777 
778                 compositeComponentBase.popComponentFromEL(context);
779             }
780             finally
781             {
782                 faceletContext.setVariableMapper(orig);
783             }
784 
785             beanInfo = (BeanInfo) compositeComponentBase.getAttributes().get(UIComponent.BEANINFO_KEY);
786         }
787         catch (IOException e)
788         {
789             throw new FacesException(e);
790         }
791         //finally
792         //{
793         //context.getAttributes().remove(BUILDING_COMPOSITE_COMPONENT_METADATA);
794         //}
795 
796         return beanInfo;
797     }
798 
799     /**
800      * Check if the current facelet applied is used to build composite component metadata.
801      *
802      * @param context
803      * @return
804      */
805     //public static boolean isBuildingCompositeComponentMetadata(FacesContext context)
806     //{
807     //    return context.getAttributes().containsKey(BUILDING_COMPOSITE_COMPONENT_METADATA);
808     //}
809 
810     /**
811      * Check if the current facelet applied is used to build view metadata.
812      *
813      * @param context
814      * @return
815      */
816     public static boolean isBuildingViewMetadata(FacesContext context)
817     {
818         return context.getAttributes().containsKey(BUILDING_VIEW_METADATA);
819     }
820 
821     public static boolean isRefreshingTransientBuild(FacesContext context)
822     {
823         return context.getAttributes().containsKey(REFRESHING_TRANSIENT_BUILD);
824     }
825 
826     public static boolean isRemovingComponentBuild(FacesContext context)
827     {
828         return context.getAttributes().containsKey(REMOVING_COMPONENTS_BUILD);
829     }
830 
831     public static boolean isMarkInitialState(FacesContext context)
832     {
833         return Boolean.TRUE.equals(context.getAttributes().get(MARK_INITIAL_STATE_KEY));
834     }
835 
836     public static boolean isRefreshTransientBuildOnPSS(FacesContext context)
837     {
838         //this include both "true" and "auto"
839         return context.getAttributes().containsKey(REFRESH_TRANSIENT_BUILD_ON_PSS);
840     }
841 
842     public static boolean isRefreshTransientBuildOnPSSAuto(FacesContext context)
843     {
844         return "auto".equalsIgnoreCase((String) context.getAttributes().get(REFRESH_TRANSIENT_BUILD_ON_PSS));
845     }
846 
847     public static boolean isCleanTransientBuildOnRestore(FacesContext context)
848     {
849         return context.getAttributes().containsKey(CLEAN_TRANSIENT_BUILD_ON_RESTORE);
850     }
851 
852     public static void cleanTransientBuildOnRestore(FacesContext context)
853     {
854         context.getAttributes().put(CLEAN_TRANSIENT_BUILD_ON_RESTORE, Boolean.TRUE);
855     }
856 
857     public static boolean isUsingPSSOnThisView(FacesContext context)
858     {
859         return context.getAttributes().containsKey(USING_PSS_ON_THIS_VIEW);
860     }
861 
862     /**
863      * In short words, this method take care of "target" an "attached object".
864      * <ul>
865      * <li>The "attached object" is instantiated by a tag handler.</li> 
866      * <li>The "target" is an object used as "marker", that exposes a List<UIComponent></li>
867      * </ul>
868      * This method should be called from some composite component tag handler, after
869      * all children of composite component has been applied.
870      */
871     @Override
872     @SuppressWarnings("unchecked")
873     public void retargetAttachedObjects(FacesContext context,
874                                         UIComponent topLevelComponent, List<AttachedObjectHandler> handlerList)
875     {
876         checkNull(context, "context");
877         checkNull(topLevelComponent, "topLevelComponent");
878         checkNull(handlerList, "handlerList");
879 
880         BeanInfo compositeComponentMetadata
881                 = (BeanInfo) topLevelComponent.getAttributes().get(UIComponent.BEANINFO_KEY);
882 
883         if (compositeComponentMetadata == null)
884         {
885             log.severe("Composite component metadata not found for: " + topLevelComponent.getClientId(context));
886             return;
887         }
888 
889         BeanDescriptor compositeComponentDescriptor = compositeComponentMetadata.getBeanDescriptor();
890 
891         List<AttachedObjectTarget> targetList = (List<AttachedObjectTarget>)
892                 compositeComponentDescriptor.getValue(AttachedObjectTarget.ATTACHED_OBJECT_TARGETS_KEY);
893 
894         if (targetList == null || targetList.isEmpty())
895         {
896             return;
897         }
898 
899         for (int i = 0, size = handlerList.size(); i < size; i++)
900         {
901             AttachedObjectHandler currentHandler = handlerList.get(i);
902             // In the spec javadoc this variable is referred as forAttributeValue, but
903             // note it is also called curTargetName
904             String forValue = currentHandler.getFor();
905             
906             // perf: targetList is always arrayList: see AttachedObjectTargetHandler.apply 
907             // and ClientBehaviorHandler.apply 
908             for (int k = 0, targetsSize = targetList.size(); k < targetsSize; k++)
909             {
910                 AttachedObjectTarget currentTarget = targetList.get(k);
911                 FaceletCompositionContext mctx = FaceletCompositionContext.getCurrentInstance();
912 
913                 if ((forValue != null && forValue.equals(currentTarget.getName())) &&
914                         ((currentTarget instanceof ActionSource2AttachedObjectTarget &&
915                                 currentHandler instanceof ActionSource2AttachedObjectHandler) ||
916                                 (currentTarget instanceof EditableValueHolderAttachedObjectTarget &&
917                                         currentHandler instanceof EditableValueHolderAttachedObjectHandler) ||
918                                 (currentTarget instanceof ValueHolderAttachedObjectTarget &&
919                                         currentHandler instanceof ValueHolderAttachedObjectHandler)))
920                 {
921                     // perf: getTargets return ArrayList - see getTargets implementations
922                     List<UIComponent> targets = currentTarget.getTargets(topLevelComponent);
923                     for (int l = 0, targetsCount = targets.size(); l < targetsCount; l++)
924                     {
925                         UIComponent component = targets.get(l);
926                         // If we found composite components when traverse the tree
927                         // we have to call this one recursively, because each composite component
928                         // should have its own AttachedObjectHandler list, filled earlier when
929                         // its tag handler is applied.
930                         if (UIComponent.isCompositeComponent(component))
931                         {
932                             // How we obtain the list of AttachedObjectHandler for
933                             // the current composite component? It should be a component
934                             // attribute or retrieved by a key inside component.getAttributes
935                             // map. Since api does not specify any attribute, we suppose
936                             // this is an implementation detail and it should be retrieved
937                             // from component attribute map.
938                             // But this is only the point of the iceberg, because we should
939                             // define how we register attached object handlers in this list.
940                             // ANS: see CompositeComponentResourceTagHandler.
941                             // The current handler should be added to the list, to be chained.
942                             // Note that the inner component should have a target with the same name
943                             // as "for" attribute
944                             mctx.addAttachedObjectHandler(component, currentHandler);
945 
946                             List<AttachedObjectHandler> handlers = mctx.getAttachedObjectHandlers(component);
947 
948                             retargetAttachedObjects(context, component, handlers);
949 
950                             handlers.remove(currentHandler);
951                         }
952                         else
953                         {
954                             currentHandler.applyAttachedObject(context, component);
955                         }
956                         if (mctx.isUsingPSSOnThisView() && mctx.isMarkInitialState())
957                         {
958                             component.markInitialState();
959                         }
960                     }
961                 }
962                 else if ((currentTarget instanceof BehaviorHolderAttachedObjectTarget &&
963                         currentHandler instanceof BehaviorHolderAttachedObjectHandler))
964                 {
965                     String eventName = ((BehaviorHolderAttachedObjectHandler) currentHandler).getEventName();
966                     boolean isDefaultEvent = ((BehaviorHolderAttachedObjectTarget) currentTarget).isDefaultEvent();
967 
968                     if ((eventName != null && eventName.equals(currentTarget.getName())) ||
969                             (eventName == null && isDefaultEvent))
970                     {
971                         List<UIComponent> targets = currentTarget.getTargets(topLevelComponent);
972                         for (int j = 0, targetssize = targets.size(); j < targetssize; j++)
973                         {
974                             UIComponent component = targets.get(j);
975                             // If we found composite components when traverse the tree
976                             // we have to call this one recursively, because each composite component
977                             // should have its own AttachedObjectHandler list, filled earlier when
978                             // its tag handler is applied.
979                             if (UIComponent.isCompositeComponent(component))
980                             {
981                                 if (currentTarget instanceof ClientBehaviorAttachedObjectTarget)
982                                 {
983                                     mctx.addAttachedObjectHandler(component,
984                                             new ClientBehaviorRedirectBehaviorAttachedObjectHandlerWrapper(
985                                                     (BehaviorHolderAttachedObjectHandler) currentHandler,
986                                                     ((ClientBehaviorAttachedObjectTarget) currentTarget).getEvent()));
987                                 }
988                                 else
989                                 {
990                                     mctx.addAttachedObjectHandler(component, currentHandler);
991                                 }
992 
993                                 List<AttachedObjectHandler> handlers = mctx.getAttachedObjectHandlers(component);
994 
995                                 retargetAttachedObjects(context, component, handlers);
996 
997                                 handlers.remove(currentHandler);
998                             }
999                             else
1000                             {
1001                                 if (currentHandler instanceof
1002                                         ClientBehaviorRedirectBehaviorAttachedObjectHandlerWrapper)
1003                                 {
1004                                     currentHandler.applyAttachedObject(context,
1005                                             new ClientBehaviorRedirectEventComponentWrapper(component,
1006                                             ((ClientBehaviorRedirectBehaviorAttachedObjectHandlerWrapper)
1007                                                     currentHandler).getWrappedEventName(), eventName));
1008                                 }
1009                                 else
1010                                 {
1011                                     currentHandler.applyAttachedObject(context, component);
1012                                 }
1013                             }
1014                             if (mctx.isUsingPSSOnThisView() && mctx.isMarkInitialState())
1015                             {
1016                                 component.markInitialState();
1017                             }
1018                         }
1019                     }
1020                 }
1021             }
1022         }
1023     }
1024 
1025     @Override
1026     public void retargetMethodExpressions(FacesContext context, UIComponent topLevelComponent)
1027     {
1028         checkNull(context, "context");
1029 
1030         BeanInfo compositeComponentMetadata
1031                 = (BeanInfo) topLevelComponent.getAttributes().get(UIComponent.BEANINFO_KEY);
1032 
1033         if (compositeComponentMetadata == null)
1034         {
1035             log.severe("Composite component metadata not found for: " + topLevelComponent.getClientId(context));
1036             return;
1037         }
1038 
1039         // "...For each attribute that is a MethodExpression..." This means we have to scan
1040         // all attributes with "method-signature" attribute and no "type" attribute
1041         // javax.faces.component._ComponentAttributesMap uses BeanInfo.getPropertyDescriptors to
1042         // traverse over it, but here the metadata returned by UIComponent.BEANINFO_KEY is available
1043         // only for composite components.
1044         // That means somewhere we need to create a custom BeanInfo object for composite components
1045         // that will be filled somewhere (theorically in ViewDeclarationLanguage.getComponentMetadata())
1046 
1047         PropertyDescriptor[] propertyDescriptors = compositeComponentMetadata.getPropertyDescriptors();
1048 
1049         ELContext elContext = (ELContext) context.getAttributes().get(FaceletContext.FACELET_CONTEXT_KEY);
1050 
1051         for (PropertyDescriptor propertyDescriptor : propertyDescriptors)
1052         {
1053             if (propertyDescriptor.getValue("type") != null)
1054             {
1055                 // This check is necessary if we have both "type" and "method-signature" set.
1056                 // In that case, "method-signature" is ignored
1057                 continue;
1058             }
1059 
1060             String attributeName = propertyDescriptor.getName();
1061             //boolean isKnownMethod = "action".equals(attributeName) || "actionListener".equals(attributeName)  
1062             //        || "validator".equals(attributeName) || "valueChangeListener".equals(attributeName);
1063 
1064             // <composite:attribute> method-signature attribute is 
1065             // ValueExpression that must evaluate to String
1066             ValueExpression methodSignatureExpression
1067                     = (ValueExpression) propertyDescriptor.getValue("method-signature");
1068             String methodSignature = null;
1069             if (methodSignatureExpression != null)
1070             {
1071                 // Check if the value expression holds a method signature
1072                 // Note that it could be null, so in that case we don't have to do anything
1073                 methodSignature = (String) methodSignatureExpression.getValue(elContext);
1074             }
1075 
1076             String targetAttributeName = null;
1077             ValueExpression targetAttributeNameVE
1078                     = (ValueExpression) propertyDescriptor.getValue("targetAttributeName");
1079             if (targetAttributeNameVE != null)
1080             {
1081                 targetAttributeName = (String) targetAttributeNameVE.getValue(context.getELContext());
1082                 if (targetAttributeName == null)
1083                 {
1084                     targetAttributeName = attributeName;
1085                 }
1086             }
1087             else
1088             {
1089                 targetAttributeName = attributeName;
1090             }
1091 
1092             boolean isKnownTargetAttributeMethod
1093                     = "action".equals(targetAttributeName) || "actionListener".equals(targetAttributeName)
1094                       || "validator".equals(targetAttributeName) || "valueChangeListener".equals(targetAttributeName);
1095 
1096             // either the attributeName has to be a knownMethod or there has to be a method-signature
1097             if (isKnownTargetAttributeMethod || methodSignature != null)
1098             {
1099                 ValueExpression targetsExpression =
1100                         (ValueExpression) propertyDescriptor.getValue("targets");
1101 
1102                 String targets = null;
1103                 // <composite:attribute> targets attribute is 
1104                 // ValueExpression that must evaluate to String
1105                 if (targetsExpression != null)
1106                 {
1107                     targets = (String) targetsExpression.getValue(elContext);
1108                 }
1109 
1110                 if (targets == null)
1111                 {
1112                     // "...let the name of the metadata element be the 
1113                     // evaluated value of the targets attribute..."
1114                     targets = attributeName;
1115                 }
1116 
1117                 FaceletCompositionContext mctx = FaceletCompositionContext.getCurrentInstance();
1118 
1119                 // If the MethodExpression attribute has been already applied, there is no need to
1120                 // handle it and it is probably a MethodExpression instance is on attribute map, so the
1121                 // inner code will cause a ClassCastException.
1122                 if (!mctx.isMethodExpressionAttributeApplied(topLevelComponent, attributeName))
1123                 {
1124 
1125                     ValueExpression attributeNameValueExpression =
1126                             (ValueExpression) topLevelComponent.getAttributes().get(attributeName);
1127 
1128                     if (attributeNameValueExpression == null)
1129                     {
1130                         // composite:attribute has a default property, so if we can't found on the
1131                         // component attribute map, we should get the default as CompositeComponentELResolver
1132                         // does.
1133                         attributeNameValueExpression = (ValueExpression) propertyDescriptor.getValue("default");
1134                         if (attributeNameValueExpression == null)
1135                         {
1136                             // It is only valid to log an error if the attribute is required
1137                             ValueExpression ve = (ValueExpression) propertyDescriptor.getValue("required");
1138                             if (ve != null)
1139                             {
1140                                 Object requiredValue = ve.getValue(elContext);
1141                                 Boolean required = null;
1142                                 if (requiredValue instanceof Boolean)
1143                                 {
1144                                     required = (Boolean) requiredValue;
1145                                 }
1146                                 else
1147                                 {
1148                                     required = Boolean.valueOf(requiredValue.toString());
1149                                 }
1150 
1151                                 if (required != null && required.booleanValue())
1152                                 {
1153                                     if (log.isLoggable(Level.SEVERE))
1154                                     {
1155                                         log.severe("attributeValueExpression not found under the key \""
1156                                                    + attributeName
1157                                                    + "\". Looking for the next attribute");
1158                                     }
1159                                 }
1160                             }
1161                             continue;
1162                         }
1163                     }
1164 
1165                     String[] targetsArray = StringUtils.splitShortString(targets, ' ');
1166                     String attributeExpressionString = attributeNameValueExpression.getExpressionString();
1167 
1168                     //Check if the stored valueExpression is a ccRedirection, to handle it properly later.
1169                     boolean ccAttrMeRedirection =
1170                             attributeNameValueExpression instanceof LocationValueExpression &&
1171                                     CompositeComponentELUtils.isCompositeComponentAttrsMethodExpression(
1172                                             attributeNameValueExpression.getExpressionString());
1173 
1174                     if (isKnownTargetAttributeMethod)
1175                     {
1176                         // To add support to #{cc.attrs.action}, #{cc.attrs.actionListener}, #{cc.attrs.validator} or
1177                         // #{cc.attrs.valueChangeListener} it is necessary to put a MethodExpression or a 
1178                         // ValueExpression pointing to the associated java method in the component attribute map.
1179                         // org.apache.myfaces.view.facelets.tag.composite.RetargetMethodExpressionRule already put
1180                         // a ValueExpression, so we only need to put a MethodExpression when a non redirecting
1181                         // expression is used (for example when a nested #{cc.attrs.xxx} is used).
1182                         if ("action".equals(targetAttributeName))
1183                         {
1184                             applyActionMethodExpressionEL(context, elContext,
1185                                     topLevelComponent, attributeName,
1186                                     attributeExpressionString, attributeNameValueExpression,
1187                                     ccAttrMeRedirection);
1188                         }
1189                         else if ("actionListener".equals(targetAttributeName))
1190                         {
1191                             applyActionListenerMethodExpressionEL(context, elContext,
1192                                     topLevelComponent, attributeName, 
1193                                     attributeExpressionString, attributeNameValueExpression, 
1194                                     ccAttrMeRedirection);
1195                         }
1196                         else if ("validator".equals(targetAttributeName))
1197                         {
1198                             applyValidatorMethodExpressionEL(context, elContext,
1199                                     topLevelComponent, attributeName,
1200                                     attributeExpressionString, attributeNameValueExpression, 
1201                                     ccAttrMeRedirection);
1202                         }
1203                         else if ("valueChangeListener".equals(targetAttributeName))
1204                         {
1205                             applyValueChangeListenerMethodExpressionEL(context, elContext,
1206                                     topLevelComponent, attributeName, 
1207                                     attributeExpressionString, attributeNameValueExpression,
1208                                     ccAttrMeRedirection);
1209                         }
1210 
1211                         UIComponent topLevelComponentBase = 
1212                             topLevelComponent.getFacet(UIComponent.COMPOSITE_FACET_NAME);
1213 
1214                         for (String target : targetsArray)
1215                         {
1216                             UIComponent innerComponent
1217                                     = ComponentSupport.findComponentChildOrFacetFrom(context, topLevelComponentBase,
1218                                                                                      target);
1219 
1220                             if (innerComponent == null)
1221                             {
1222                                 continue;
1223                             }
1224 
1225                             if (isCompositeComponentRetarget(context, innerComponent, targetAttributeName))
1226                             {
1227                                 innerComponent.getAttributes().put(targetAttributeName, attributeNameValueExpression);
1228 
1229                                 mctx.clearMethodExpressionAttribute(innerComponent, targetAttributeName);
1230 
1231                                 retargetMethodExpressions(context, innerComponent);
1232                                 if (mctx.isUsingPSSOnThisView() && mctx.isMarkInitialState())
1233                                 {
1234                                     innerComponent.markInitialState();
1235                                 }
1236                             }
1237                             else
1238                             {
1239                                 if ("action".equals(targetAttributeName))
1240                                 {
1241                                     applyActionMethodExpressionTarget(context, mctx, elContext,
1242                                             topLevelComponentBase, innerComponent, 
1243                                             attributeName, targetAttributeName, 
1244                                             attributeExpressionString, attributeNameValueExpression, 
1245                                             ccAttrMeRedirection);
1246                                     if (mctx.isUsingPSSOnThisView() && mctx.isMarkInitialState())
1247                                     {
1248                                         innerComponent.markInitialState();
1249                                     }
1250                                 }
1251                                 else if ("actionListener".equals(targetAttributeName))
1252                                 {
1253                                     applyActionListenerMethodExpressionTarget(context, mctx, elContext, 
1254                                             topLevelComponentBase, innerComponent, 
1255                                             attributeName, targetAttributeName, 
1256                                             attributeExpressionString, attributeNameValueExpression, 
1257                                             ccAttrMeRedirection);
1258                                     if (mctx.isUsingPSSOnThisView() && mctx.isMarkInitialState())
1259                                     {
1260                                         innerComponent.markInitialState();
1261                                     }
1262                                 }
1263                                 else if ("validator".equals(targetAttributeName))
1264                                 {
1265                                     applyValidatorMethodExpressionTarget(context, mctx, elContext,
1266                                             topLevelComponentBase, innerComponent, 
1267                                             attributeName, targetAttributeName, 
1268                                             attributeExpressionString, attributeNameValueExpression, 
1269                                             ccAttrMeRedirection);
1270                                     if (mctx.isUsingPSSOnThisView() && mctx.isMarkInitialState())
1271                                     {
1272                                         innerComponent.markInitialState();
1273                                     }
1274                                 }
1275                                 else if ("valueChangeListener".equals(targetAttributeName))
1276                                 {
1277                                     applyValueChangeListenerMethodExpressionTarget(context, mctx, elContext,
1278                                             topLevelComponentBase, innerComponent, 
1279                                             attributeName, targetAttributeName,
1280                                             attributeExpressionString, attributeNameValueExpression,
1281                                             ccAttrMeRedirection);
1282                                     if (mctx.isUsingPSSOnThisView() && mctx.isMarkInitialState())
1283                                     {
1284                                         innerComponent.markInitialState();
1285                                     }
1286                                 }
1287                             }
1288                         }
1289                     }
1290                     else
1291                     {
1292                         MethodExpression methodExpression = null;
1293                         // composite:attribute targets property only has sense for action, actionListener,
1294                         // validator or valueChangeListener. This means we have to retarget the method expression
1295                         // to the topLevelComponent.
1296 
1297                         // Since a MethodExpression has no state, we can use it multiple times without problem, so
1298                         // first create it here.
1299                         methodSignature = methodSignature.trim();
1300                         methodExpression = context.getApplication().getExpressionFactory().
1301                                 createMethodExpression(elContext,
1302                                         attributeExpressionString, 
1303                                         FaceletsViewDeclarationLanguageUtils.getReturnType(methodSignature),
1304                                         FaceletsViewDeclarationLanguageUtils.getParameters(methodSignature));
1305 
1306                         methodExpression = reWrapMethodExpression(methodExpression, attributeNameValueExpression);
1307 
1308                         applyMethodExpression(context, mctx, topLevelComponent, attributeName, 
1309                                 targetAttributeName, attributeNameValueExpression, methodExpression, 
1310                                 ccAttrMeRedirection, targetsArray);
1311                     }
1312                     mctx.markMethodExpressionAttribute(topLevelComponent, attributeName);
1313                 }
1314 
1315                 // We need to remove the previous ValueExpression, to prevent some possible
1316                 // confusion when the same value is retrieved from the attribute map.
1317                 topLevelComponent.setValueExpression(attributeName, null);
1318             }
1319         }
1320     }
1321     
1322     private void applyActionMethodExpressionEL(FacesContext context, 
1323                                                ELContext elContext, 
1324                                                UIComponent topLevelComponent, 
1325                                                String attributeName,
1326                                                String attributeExpressionString, 
1327                                                ValueExpression attributeNameValueExpression, 
1328                                                boolean ccAttrMeRedirection)
1329     {
1330         // target is ActionSource2
1331         MethodExpression methodExpression = reWrapMethodExpression(context.getApplication().getExpressionFactory().
1332                 createMethodExpression(elContext,
1333                         attributeExpressionString, null,
1334                         EMPTY_CLASS_ARRAY), attributeNameValueExpression);
1335 
1336         //Store the method expression to the topLevelComponent to allow reference it through EL
1337         if (!ccAttrMeRedirection)
1338         {
1339             //Replace it with a method expression
1340             topLevelComponent.getAttributes().put(attributeName, methodExpression);
1341         }
1342         // Otherwise keep the current ValueExpression,
1343         // because it will be used chain other value expressions
1344     }
1345 
1346     private void applyActionListenerMethodExpressionEL(FacesContext context, 
1347                                                        ELContext elContext, 
1348                                                        UIComponent topLevelComponent, 
1349                                                        String attributeName,
1350                                                        String attributeExpressionString, 
1351                                                        ValueExpression attributeNameValueExpression, 
1352                                                        boolean ccAttrMeRedirection)
1353     {
1354         // target is ActionSource2
1355         MethodExpression methodExpression = reWrapMethodExpression(context.getApplication().getExpressionFactory().
1356                 createMethodExpression(elContext,
1357                         attributeExpressionString, Void.TYPE, ACTION_LISTENER_SIGNATURE),
1358                         attributeNameValueExpression);
1359 
1360         MethodExpression methodExpression2 = reWrapMethodExpression(context.getApplication().getExpressionFactory().
1361                 createMethodExpression(elContext,
1362                         attributeExpressionString, Void.TYPE, EMPTY_CLASS_ARRAY),
1363                         attributeNameValueExpression);
1364 
1365         //Store the method expression to the topLevelComponent to allow reference it through EL
1366         if (!ccAttrMeRedirection)
1367         {
1368             //Replace it with a method expression
1369             topLevelComponent.getAttributes().put(attributeName,
1370                     new MethodExpressionMethodExpression(methodExpression, methodExpression2));
1371         }
1372         // Otherwise keep the current ValueExpression,
1373         // because it will be used chain other value expressions
1374     }
1375     
1376     private void applyValidatorMethodExpressionEL(FacesContext context, 
1377                                                   ELContext elContext, 
1378                                                   UIComponent topLevelComponent, 
1379                                                   String attributeName,
1380                                                   String attributeExpressionString, 
1381                                                   ValueExpression attributeNameValueExpression, 
1382                                                   boolean ccAttrMeRedirection)
1383     {
1384         // target is EditableValueHolder
1385         MethodExpression methodExpression = reWrapMethodExpression(context.getApplication().getExpressionFactory().
1386                 createMethodExpression(elContext,
1387                         attributeExpressionString, Void.TYPE,
1388                         VALIDATOR_SIGNATURE), attributeNameValueExpression);
1389 
1390         //Store the method expression to the topLevelComponent to allow reference it through EL
1391         if (!ccAttrMeRedirection)
1392         {
1393             //Replace it with a method expression
1394             topLevelComponent.getAttributes().put(attributeName, methodExpression);
1395         }
1396         // Otherwise keep the current ValueExpression,
1397         // because it will be used chain other value expressions
1398     }
1399     
1400     private void applyValueChangeListenerMethodExpressionEL(FacesContext context, 
1401                                                             ELContext elContext, 
1402                                                             UIComponent topLevelComponent, 
1403                                                             String attributeName,
1404                                                             String attributeExpressionString, 
1405                                                             ValueExpression attributeNameValueExpression, 
1406                                                             boolean ccAttrMeRedirection)
1407     {
1408         // target is EditableValueHolder
1409         MethodExpression methodExpression = reWrapMethodExpression(context.getApplication().getExpressionFactory().
1410                 createMethodExpression(elContext,
1411                         attributeExpressionString, Void.TYPE,
1412                         VALUE_CHANGE_LISTENER_SIGNATURE), attributeNameValueExpression);
1413 
1414         MethodExpression methodExpression2 = reWrapMethodExpression(context.getApplication().getExpressionFactory().
1415                 createMethodExpression(elContext,
1416                         attributeExpressionString, Void.TYPE,
1417                         EMPTY_CLASS_ARRAY), attributeNameValueExpression);
1418 
1419         //Store the method expression to the topLevelComponent to allow reference it through EL
1420         if (!ccAttrMeRedirection)
1421         {
1422             //Replace it with a method expression
1423             topLevelComponent.getAttributes().put(attributeName,
1424                     new MethodExpressionMethodExpression(methodExpression, methodExpression2));
1425         }
1426         // Otherwise keep the current ValueExpression, because it will be used chain other value expressions
1427     }
1428     
1429     private void applyActionMethodExpressionTarget(FacesContext context, FaceletCompositionContext mctx,
1430                                                    ELContext elContext, 
1431                                                    UIComponent topLevelComponent,
1432                                                    UIComponent innerComponent,
1433                                                    String attributeName,
1434                                                    String targetAttributeName,
1435                                                    String attributeExpressionString,
1436                                                    ValueExpression attributeNameValueExpression,
1437                                                    boolean ccAttrMeRedirection)
1438     {
1439         // target is ActionSource2
1440         MethodExpression methodExpression
1441                 = reWrapMethodExpression(context.getApplication().getExpressionFactory().
1442                 createMethodExpression(elContext,
1443                         attributeExpressionString, null, EMPTY_CLASS_ARRAY),
1444                         attributeNameValueExpression);
1445 
1446         // If it is a redirection, a wrapper is used to
1447         // locate the right instance and call it properly.
1448         if (ccAttrMeRedirection)
1449         {
1450             ((ActionSource2) innerComponent).setActionExpression(
1451                     new ValueExpressionMethodExpression(attributeNameValueExpression));
1452         }
1453         else
1454         {
1455             ((ActionSource2) innerComponent).setActionExpression(methodExpression);
1456         }
1457     }
1458 
1459     private void applyActionListenerMethodExpressionTarget(FacesContext context, FaceletCompositionContext mctx,
1460             ELContext elContext, 
1461             UIComponent topLevelComponent,
1462             UIComponent innerComponent,
1463             String attributeName,
1464             String targetAttributeName,
1465             String attributeExpressionString,
1466             ValueExpression attributeNameValueExpression,
1467             boolean ccAttrMeRedirection)
1468     {
1469         //First try to remove any prevous target if any
1470         ActionListener o = (ActionListener)
1471                 mctx.removeMethodExpressionTargeted(innerComponent, targetAttributeName);
1472         if (o != null)
1473         {
1474             ((ActionSource2) innerComponent).removeActionListener(o);
1475         }
1476 
1477         // target is ActionSource2
1478         ActionListener actionListener = null;
1479         // If it is a redirection, a wrapper is used to locate the right instance and call
1480         //it properly.
1481         if (ccAttrMeRedirection)
1482         {
1483             actionListener = new RedirectMethodExpressionValueExpressionActionListener(
1484                                          attributeNameValueExpression);
1485         }
1486         else
1487         {
1488             MethodExpression methodExpression = reWrapMethodExpression(context.getApplication().getExpressionFactory().
1489                     createMethodExpression(elContext,
1490                        attributeExpressionString, Void.TYPE, ACTION_LISTENER_SIGNATURE), attributeNameValueExpression);
1491 
1492             MethodExpression methodExpression2 = reWrapMethodExpression(context.getApplication().getExpressionFactory().
1493                     createMethodExpression(elContext,
1494                             attributeExpressionString, Void.TYPE, EMPTY_CLASS_ARRAY), attributeNameValueExpression);
1495 
1496             if (mctx.isUsingPSSOnThisView())
1497             {
1498                 actionListener = new PartialMethodExpressionActionListener(methodExpression, methodExpression2);
1499             }
1500             else
1501             {
1502                 actionListener = new MethodExpressionActionListener(methodExpression, methodExpression2);
1503             }
1504         }
1505         ((ActionSource2) innerComponent).addActionListener(actionListener);
1506         mctx.addMethodExpressionTargeted(innerComponent, targetAttributeName, actionListener);
1507     }
1508     
1509     private void applyValidatorMethodExpressionTarget(FacesContext context, FaceletCompositionContext mctx,
1510             ELContext elContext, 
1511             UIComponent topLevelComponent,
1512             UIComponent innerComponent,
1513             String attributeName,
1514             String targetAttributeName,
1515             String attributeExpressionString,
1516             ValueExpression attributeNameValueExpression,
1517             boolean ccAttrMeRedirection)
1518     {
1519         //First try to remove any prevous target if any
1520         Validator o = (Validator) mctx.removeMethodExpressionTargeted(innerComponent, targetAttributeName);
1521         if (o != null)
1522         {
1523             ((EditableValueHolder) innerComponent).removeValidator(o);
1524         }
1525 
1526         // target is EditableValueHolder
1527         Validator validator = null;
1528         // If it is a redirection, a wrapper is used to locate the right instance and call it properly.
1529         if (ccAttrMeRedirection)
1530         {
1531             validator = new RedirectMethodExpressionValueExpressionValidator(attributeNameValueExpression);
1532         }
1533         else
1534         {
1535             MethodExpression methodExpression = reWrapMethodExpression(context.getApplication().getExpressionFactory().
1536                     createMethodExpression(elContext,
1537                             attributeExpressionString, Void.TYPE,
1538                             VALIDATOR_SIGNATURE), attributeNameValueExpression);
1539 
1540             if (mctx.isUsingPSSOnThisView())
1541             {
1542                 validator = new PartialMethodExpressionValidator(methodExpression);
1543             }
1544             else
1545             {
1546                 validator = new MethodExpressionValidator(methodExpression);
1547             }
1548         }
1549         ((EditableValueHolder) innerComponent).addValidator(validator);
1550         mctx.addMethodExpressionTargeted(innerComponent, targetAttributeName, validator);
1551     }
1552     
1553     private void applyValueChangeListenerMethodExpressionTarget(FacesContext context, FaceletCompositionContext mctx,
1554             ELContext elContext, 
1555             UIComponent topLevelComponent,
1556             UIComponent innerComponent,
1557             String attributeName,
1558             String targetAttributeName,
1559             String attributeExpressionString,
1560             ValueExpression attributeNameValueExpression,
1561             boolean ccAttrMeRedirection)
1562     {
1563         ValueChangeListener o = (ValueChangeListener) mctx.removeMethodExpressionTargeted(
1564                 innerComponent, targetAttributeName);
1565         if (o != null)
1566         {
1567             ((EditableValueHolder) innerComponent).removeValueChangeListener(o);
1568         }
1569 
1570         // target is EditableValueHolder
1571         ValueChangeListener valueChangeListener = null;
1572         // If it is a redirection, a wrapper is used to locate the right instance and call it properly.
1573         if (ccAttrMeRedirection)
1574         {
1575             valueChangeListener = new RedirectMethodExpressionValueExpressionValueChangeListener(
1576                     attributeNameValueExpression);
1577         }
1578         else
1579         {
1580             MethodExpression methodExpression = reWrapMethodExpression(context.getApplication().getExpressionFactory().
1581                     createMethodExpression(elContext,
1582                             attributeExpressionString, Void.TYPE,
1583                             VALUE_CHANGE_LISTENER_SIGNATURE), attributeNameValueExpression);
1584 
1585             MethodExpression methodExpression2 = reWrapMethodExpression(context.getApplication().getExpressionFactory().
1586                     createMethodExpression(elContext,
1587                             attributeExpressionString, Void.TYPE,
1588                             EMPTY_CLASS_ARRAY), attributeNameValueExpression);
1589 
1590             if (mctx.isUsingPSSOnThisView())
1591             {
1592                 valueChangeListener = new PartialMethodExpressionValueChangeListener(
1593                         methodExpression, methodExpression2);
1594             }
1595             else
1596             {
1597                 valueChangeListener = new MethodExpressionValueChangeListener(methodExpression, methodExpression2);
1598             }
1599         }
1600         ((EditableValueHolder) innerComponent).addValueChangeListener(valueChangeListener);
1601         mctx.addMethodExpressionTargeted(innerComponent, targetAttributeName, valueChangeListener);
1602     }
1603     
1604     private void applyMethodExpression(FacesContext context, FaceletCompositionContext mctx, 
1605             UIComponent topLevelComponent,
1606             String attributeName,
1607             String targetAttributeName,
1608             ValueExpression attributeNameValueExpression,
1609             MethodExpression methodExpression,
1610             boolean ccAttrMeRedirection,
1611             String[] targetsArray)
1612     {
1613         UIComponent topLevelComponentBase = topLevelComponent.getFacet(
1614                 UIComponent.COMPOSITE_FACET_NAME);
1615 
1616         for (String target : targetsArray)
1617         {
1618             UIComponent innerComponent = ComponentSupport.findComponentChildOrFacetFrom(context, 
1619                     topLevelComponentBase, target);
1620 
1621             if (innerComponent == null)
1622             {
1623                 continue;
1624             }
1625 
1626             // If a component is found, that means the expression should be retarget to the
1627             // components related
1628             if (isCompositeComponentRetarget(context, innerComponent, targetAttributeName))
1629             {
1630                 innerComponent.getAttributes().put(targetAttributeName, attributeNameValueExpression);
1631 
1632                 mctx.clearMethodExpressionAttribute(innerComponent, targetAttributeName);
1633 
1634                 retargetMethodExpressions(context, innerComponent);
1635                 
1636                 if (mctx.isUsingPSSOnThisView() && mctx.isMarkInitialState())
1637                 {
1638                     //retargetMethodExpression occur on build view time, so it is safe to call markInitiaState here
1639                     innerComponent.markInitialState();
1640                 }
1641             }
1642             else
1643             {
1644                 //Put the retarget
1645                 if (ccAttrMeRedirection)
1646                 {
1647                     // Since we require here a method expression, it is necessary to wrap 
1648                     // the ValueExpression into a MethodExpression that handles redirection.
1649                     innerComponent.getAttributes().put(targetAttributeName, 
1650                             new ValueExpressionMethodExpression(attributeNameValueExpression));
1651                 }
1652                 else
1653                 {
1654                     innerComponent.getAttributes().put(targetAttributeName, methodExpression);
1655                 }
1656                 if (mctx.isUsingPSSOnThisView() && mctx.isMarkInitialState())
1657                 {
1658                     innerComponent.markInitialState();
1659                 }
1660             }
1661         }
1662         //Store the method expression to the topLevelComponent to allow reference it through EL
1663         if (!ccAttrMeRedirection)
1664         {
1665             //Replace it with a method expression
1666             topLevelComponent.getAttributes().put(attributeName, methodExpression);
1667         }
1668         // Othewise keep the current ValueExpression, because it will be used chain other value 
1669         // expressions
1670     }
1671 
1672 
1673     private boolean isCompositeComponentRetarget(FacesContext context, UIComponent component, String attributeName)
1674     {
1675         if (UIComponent.isCompositeComponent(component))
1676         {
1677             BeanInfo compositeComponentMetadata = (BeanInfo) component.getAttributes().get(UIComponent.BEANINFO_KEY);
1678 
1679             PropertyDescriptor[] propertyDescriptors = compositeComponentMetadata.getPropertyDescriptors();
1680 
1681             ELContext elContext = (ELContext) context.getAttributes().get(FaceletContext.FACELET_CONTEXT_KEY);
1682 
1683             for (PropertyDescriptor propertyDescriptor : propertyDescriptors)
1684             {
1685                 if (propertyDescriptor.getValue("type") != null)
1686                 {
1687                     // This check is necessary if we have both "type" and "method-signature" set.
1688                     // In that case, "method-signature" is ignored
1689                     continue;
1690                 }
1691 
1692                 if (attributeName.equals(propertyDescriptor.getName()))
1693                 {
1694                     //boolean isKnownMethod = "action".equals(attributeName) || "actionListener".equals(attributeName)  
1695                     //|| "validator".equals(attributeName) || "valueChangeListener".equals(attributeName);
1696 
1697                     // <composite:attribute> method-signature attribute is 
1698                     // ValueExpression that must evaluate to String
1699                     ValueExpression methodSignatureExpression
1700                             = (ValueExpression) propertyDescriptor.getValue("method-signature");
1701                     String methodSignature = null;
1702                     if (methodSignatureExpression != null)
1703                     {
1704                         // Check if the value expression holds a method signature
1705                         // Note that it could be null, so in that case we don't have to do anything
1706                         methodSignature = (String) methodSignatureExpression.getValue(elContext);
1707                     }
1708 
1709                     String targetAttributeName = null;
1710                     ValueExpression targetAttributeNameVE = (ValueExpression) 
1711                         propertyDescriptor.getValue("targetAttributeName");
1712                     if (targetAttributeNameVE != null)
1713                     {
1714                         targetAttributeName = (String) targetAttributeNameVE.getValue(context.getELContext());
1715                         if (targetAttributeName == null)
1716                         {
1717                             targetAttributeName = attributeName;
1718                         }
1719                     }
1720                     else
1721                     {
1722                         targetAttributeName = attributeName;
1723                     }
1724 
1725                     boolean isKnownTargetAttributeMethod = "action".equals(targetAttributeName)
1726                             || "actionListener".equals(targetAttributeName)
1727                             || "validator".equals(targetAttributeName)
1728                             || "valueChangeListener".equals(targetAttributeName);
1729 
1730                     // either the attributeName has to be a knownMethod or there has to be a method-signature
1731                     if (isKnownTargetAttributeMethod || methodSignature != null)
1732                     {
1733                         if ("action".equals(targetAttributeName))
1734                         {
1735                             return !(component instanceof ActionSource2);
1736                         }
1737                         else if ("actionListener".equals(targetAttributeName))
1738                         {
1739                             return !(component instanceof ActionSource2);
1740                         }
1741                         else if ("validator".equals(targetAttributeName))
1742                         {
1743                             return !(component instanceof EditableValueHolder);
1744                         }
1745                         else if ("valueChangeListener".equals(targetAttributeName))
1746                         {
1747                             return !(component instanceof EditableValueHolder);
1748                         }
1749                         else
1750                         {
1751                             return true;
1752                         }
1753                     }
1754                 }
1755             }
1756             return false;
1757         }
1758         else
1759         {
1760             return false;
1761         }
1762     }
1763 
1764     @SuppressWarnings("unchecked")
1765     private MethodExpression reWrapMethodExpression(MethodExpression createdMethodExpression,
1766                                                     ValueExpression originalValueExpression)
1767     {
1768         if (originalValueExpression instanceof LocationValueExpression)
1769         {
1770             return new LocationMethodExpression(
1771                     ((LocationValueExpression) originalValueExpression).getLocation(),
1772                     reWrapMethodExpression(createdMethodExpression,
1773                             ((LocationValueExpression) originalValueExpression).getWrapped()),
1774                     ((LocationValueExpression) originalValueExpression).getCCLevel());
1775         }
1776         else if (originalValueExpression instanceof FacesWrapper &&
1777                 ((FacesWrapper) originalValueExpression).getWrapped() instanceof ValueExpression)
1778         {
1779             return reWrapMethodExpression(createdMethodExpression,
1780                     (ValueExpression) ((FacesWrapper) originalValueExpression).getWrapped());
1781         }
1782         else
1783         {
1784             return createdMethodExpression;
1785         }
1786     }
1787 
1788     /**
1789      * {@inheritDoc}
1790      */
1791     @Override
1792     public Resource getScriptComponentResource(FacesContext context, Resource componentResource)
1793     {
1794         checkNull(context, "context");
1795         checkNull(componentResource, "componentResource");
1796         // TODO Auto-generated method stub
1797         return null;
1798     }
1799 
1800     /**
1801      * {@inheritDoc}
1802      */
1803     @Override
1804     public StateManagementStrategy getStateManagementStrategy(FacesContext context, String viewId)
1805     {
1806         // Use partial state saving strategy only if javax.faces.PARTIAL_STATE_SAVING is "true" and
1807         // the current view is not on javax.faces.FULL_STATE_SAVING_VIEW_IDS.
1808         if (_partialStateSaving && _stateMgmtStrategy == null)
1809         {
1810             _stateMgmtStrategy = new DefaultFaceletsStateManagementStrategy(context);
1811         }
1812 
1813         return _usePartialStateSavingOnThisView(viewId) ? _stateMgmtStrategy : null;
1814     }
1815 
1816     /**
1817      * {@inheritDoc}
1818      */
1819     @Override
1820     public ViewMetadata getViewMetadata(FacesContext context, String viewId)
1821     {
1822         checkNull(context, "facesContext");
1823         checkNull(viewId, "viewId");
1824         return new FaceletViewMetadata(viewId);
1825     }
1826 
1827     /**
1828      * {@inheritDoc}
1829      */
1830     @Override
1831     public void renderView(FacesContext context, UIViewRoot view) throws IOException
1832     {
1833         if (!view.isRendered())
1834         {
1835             return;
1836         }
1837 
1838         // log request
1839         if (log.isLoggable(Level.FINE))
1840         {
1841             log.fine("Rendering View: " + view.getViewId());
1842         }
1843 
1844         try
1845         {
1846             // build view - but not if we're in "buildBeforeRestore"
1847             // land and we've already got a populated view. Note
1848             // that this optimizations breaks if there's a "c:if" in
1849             // the page that toggles as a result of request processing -
1850             // should that be handled? Or
1851             // is this optimization simply so minor that it should just
1852             // be trimmed altogether?
1853             // See JSF 2.0 spec section 2.2.6, buildView is called before
1854             // Render Response
1855             //if (!isFilledView(context, view))
1856             //{
1857             //    buildView(context, view);
1858             //}
1859 
1860             // setup writer and assign it to the context
1861             ResponseWriter origWriter = createResponseWriter(context);
1862 
1863             ExternalContext extContext = context.getExternalContext();
1864             Writer outputWriter = extContext.getResponseOutputWriter();
1865 
1866             StateWriter stateWriter = new StateWriter(outputWriter, 1024, context);
1867             try
1868             {
1869                 ResponseWriter writer = origWriter.cloneWithWriter(stateWriter);
1870                 try
1871                 {
1872                     context.setResponseWriter(writer);
1873 
1874                     StateManager stateMgr = context.getApplication().getStateManager();
1875                     // force creation of session if saving state there
1876                     // -= Leonardo Uribe =- Do this does not have any sense!. The only reference
1877                     // about these lines are on http://java.net/projects/facelets/sources/svn/revision/376
1878                     // and it says: "fixed lazy session instantiation with eager response commit"
1879                     // This code is obviously to prevent this exception:
1880                     // java.lang.IllegalStateException: Cannot create a session after the response has been committed
1881                     // But in theory if that so, StateManager.saveState must happen before writer.close() is called,
1882                     // which can be done very easily.
1883                     //if (!stateMgr.isSavingStateInClient(context))
1884                     //{
1885                     //    extContext.getSession(true);
1886                     //}
1887 
1888                     // render the view to the response
1889                     writer.startDocument();
1890 
1891                     view.encodeAll(context);
1892 
1893                     writer.endDocument();
1894 
1895                     // finish writing
1896                     // -= Leonardo Uribe =- This does not has sense too, because that's the reason
1897                     // of the try/finally block. In practice, it only forces the close of the tag 
1898                     // in HtmlResponseWriter if necessary, but according to the spec, this should
1899                     // be done using writer.flush() instead.
1900                     // writer.close();
1901 
1902                     // flush to origWriter
1903                     if (stateWriter.isStateWritten())
1904                     {
1905                         // Call this method to force close the tag if necessary.
1906                         // The spec javadoc says this: 
1907                         // "... Flush any ouput buffered by the output method to the underlying 
1908                         // Writer or OutputStream. This method will not flush the underlying 
1909                         // Writer or OutputStream; it simply clears any values buffered by this 
1910                         // ResponseWriter. ..."
1911                         writer.flush();
1912 
1913                         // =-= markoc: STATE_KEY is in output ONLY if 
1914                         // stateManager.isSavingStateInClient(context)is true - see
1915                         // org.apache.myfaces.application.ViewHandlerImpl.writeState(FacesContext)
1916                         // TODO this class and ViewHandlerImpl contain same constant <!--@@JSF_FORM_STATE_MARKER@@-->
1917                         Object stateObj = stateMgr.saveView(context);
1918                         String content = stateWriter.getAndResetBuffer();
1919                         int end = content.indexOf(STATE_KEY);
1920                         // See if we can find any trace of the saved state.
1921                         // If so, we need to perform token replacement
1922                         if (end >= 0)
1923                         {
1924                             // save state
1925                             String stateStr;
1926                             if (view.isTransient())
1927                             {
1928                                 // Force state saving
1929                                 stateMgr.writeState(context, stateObj);
1930                                 stateStr = stateWriter.getAndResetBuffer();
1931                             }
1932                             else if (stateObj == null)
1933                             {
1934                                 stateStr = null;
1935                             }
1936                             else
1937                             {
1938                                 stateMgr.writeState(context, stateObj);
1939                                 stateStr = stateWriter.getAndResetBuffer();
1940                             }
1941 
1942                             int start = 0;
1943 
1944                             while (end != -1)
1945                             {
1946                                 origWriter.write(content, start, end - start);
1947                                 if (stateStr != null)
1948                                 {
1949                                     origWriter.write(stateStr);
1950                                 }
1951                                 start = end + STATE_KEY_LEN;
1952                                 end = content.indexOf(STATE_KEY, start);
1953                             }
1954 
1955                             origWriter.write(content, start, content.length() - start);
1956                             // No trace of any saved state, so we just need to flush
1957                             // the buffer
1958                         }
1959                         else
1960                         {
1961                             origWriter.write(content);
1962                         }
1963                     }
1964                     else if (stateWriter.isStateWrittenWithoutWrapper())
1965                     {
1966                         // The state token has been written but the state has not been
1967                         // saved yet.
1968                         stateMgr.saveView(context);
1969                     }
1970                     else
1971                     {
1972                         // GET case without any form that trigger state saving.
1973                         // Try to store it into cache.
1974                         if (_viewPoolProcessor != null && 
1975                             _viewPoolProcessor.isViewPoolEnabledForThisView(context, view))
1976                         {
1977                             ViewDeclarationLanguage vdl = context.getApplication().
1978                                     getViewHandler().getViewDeclarationLanguage(
1979                                         context, view.getViewId());
1980 
1981                             if (ViewDeclarationLanguage.FACELETS_VIEW_DECLARATION_LANGUAGE_ID.equals(
1982                                     vdl.getId()))
1983                             {
1984                                 StateManagementStrategy sms = vdl.getStateManagementStrategy(
1985                                         context, view.getId());
1986                                 if (sms != null)
1987                                 {
1988                                     context.getAttributes().put(ViewPoolProcessor.FORCE_HARD_RESET, Boolean.TRUE);
1989 
1990                                     // Force indirectly to store the map in the cache
1991                                     try
1992                                     {
1993                                         Object state = sms.saveView(context);
1994                                     }
1995                                     finally
1996                                     {
1997                                         context.getAttributes().remove(ViewPoolProcessor.FORCE_HARD_RESET);
1998                                     }
1999 
2000                                     // Clear the calculated value from the application map
2001                                     context.getAttributes().remove(SERIALIZED_VIEW_REQUEST_ATTR);
2002                                 }
2003                             }
2004                         }
2005                     }
2006                 }
2007                 finally
2008                 {
2009                     // The Facelets implementation must close the writer used to write the response
2010                     writer.close();
2011                 }
2012             }
2013             finally
2014             {
2015                 stateWriter.release(context);
2016             }
2017         }
2018         catch (FileNotFoundException fnfe)
2019         {
2020             handleFaceletNotFound(context, view.getViewId());
2021         }
2022         catch (Exception e)
2023         {
2024             handleRenderException(context, e);
2025         }
2026     }
2027     
2028     private static final String SERIALIZED_VIEW_REQUEST_ATTR = 
2029         StateManagerImpl.class.getName() + ".SERIALIZED_VIEW";
2030 
2031     /**
2032      * {@inheritDoc}
2033      */
2034     @Override
2035     public UIViewRoot createView(FacesContext context, String viewId)
2036     {
2037         checkNull(viewId, "viewId");
2038         // we have to check for a possible debug request
2039         if (UIDebug.debugRequest(context))
2040         {
2041             // the current request is a debug request, so we don't need
2042             // to create a view, since the output has already been written
2043             // in UIDebug.debugRequest() and facesContext.responseComplete()
2044             // has been called.
2045             return null;
2046         }
2047         else
2048         {
2049             UIViewRoot root = super.createView(context, viewId);
2050             if (root != null)
2051             {
2052                 //Ensure calculateResourceLibraryContracts() can be decorated
2053                 ViewDeclarationLanguage vdl = context.getApplication().getViewHandler().
2054                     getViewDeclarationLanguage(context, viewId);
2055                 List<String> contracts = vdl.calculateResourceLibraryContracts(
2056                     context, root.getViewId() != null ? root.getViewId() : viewId);
2057                 context.setResourceLibraryContracts(contracts);
2058             }
2059             return root;
2060         }
2061     }
2062 
2063     /**
2064      * {@inheritDoc}
2065      */
2066     @Override
2067     public UIViewRoot restoreView(FacesContext context, String viewId)
2068     {
2069         checkNull(viewId, "viewId");
2070         // Currently there is no way, in which UIDebug.debugRequest(context)
2071         // can create debug information and return true at this point,
2072         // because this method is only accessed if the current request
2073         // is a postback, which will never be true for a debug page.
2074         // The only point where valid debug output can be produced by now
2075         // is in createView() -= Jakob Korherr =-
2076         //if (UIDebug.debugRequest(context))
2077         //{
2078         //    return new UIViewRoot();
2079         //}
2080 
2081         //else if (!_buildBeforeRestore)
2082         //{
2083         
2084         // When partial state saving is not used, createView() is not called. To ensure
2085         // everything is in place it is necessary to calculate the resource library 
2086         // contracts and set them into facesContext.
2087         ViewDeclarationLanguage vdl = context.getApplication().getViewHandler().
2088             getViewDeclarationLanguage(context, viewId);
2089         List<String> contracts = vdl.calculateResourceLibraryContracts(
2090             context, viewId);
2091         context.setResourceLibraryContracts(contracts);
2092         
2093         // JSF 2.2 stateless views
2094         // We need to check if the incoming view is stateless or not and if that so rebuild it here
2095         // note we cannot do this in DefaultFaceletsStateManagementStrategy because it is only used
2096         // when PSS is enabled, but stateless views can be used without PSS. If the view is stateless,
2097         // there is no need to ask to the StateManager.
2098         Application application = context.getApplication();
2099         ViewHandler applicationViewHandler = application.getViewHandler();
2100         String renderKitId = applicationViewHandler.calculateRenderKitId(context);
2101 
2102         ResponseStateManager manager = getRenderKitFactory().getRenderKit(
2103             context, renderKitId).getResponseStateManager();
2104         
2105         if (manager.isStateless(context, viewId))
2106         {
2107             // Per the spec: build the view.
2108             UIViewRoot view = null;
2109             try
2110             {
2111                 ViewMetadata metadata = vdl.getViewMetadata (context, viewId);
2112                 if (metadata != null)
2113                 {
2114                     view = metadata.createMetadataView(context);
2115                 }
2116                 if (view == null)
2117                 {
2118                     view = context.getApplication().getViewHandler().createView(context, viewId);
2119                 }
2120                 context.setViewRoot (view); 
2121                 boolean oldContextEventState = context.isProcessingEvents();
2122                 try 
2123                 {
2124                     context.setProcessingEvents (true);
2125                     vdl.buildView (context, view);
2126                 }
2127                 finally
2128                 {
2129                     context.setProcessingEvents (oldContextEventState);
2130                 } 
2131             }
2132             catch (Throwable e)
2133             {
2134                 throw new FacesException ("unable to create view \"" + viewId + "\"", e);
2135             }
2136             return view;
2137         }
2138         else
2139         {
2140             return super.restoreView(context, viewId);
2141         }
2142     }
2143 
2144     /**
2145      * {@inheritDoc}
2146      */
2147     @Override
2148     protected String calculateViewId(FacesContext context, String viewId)
2149     {
2150         if (_cachedViewHandlerSupport == null)
2151         {
2152             _cachedViewHandlerSupport = new DefaultViewHandlerSupport();
2153         }
2154 
2155         return _cachedViewHandlerSupport.calculateViewId(context, viewId);
2156     }
2157 
2158     /**
2159      * Creates the Facelet page compiler.
2160      *
2161      * @param context
2162      *            the current FacesContext
2163      *
2164      * @return the application's Facelet page compiler
2165      */
2166     protected Compiler createCompiler(FacesContext context)
2167     {
2168         Compiler compiler = new SAXCompiler();
2169 
2170         compiler.setDevelopmentProjectStage(context.isProjectStage(ProjectStage.Development));
2171 
2172         loadLibraries(context, compiler);
2173         loadDecorators(context, compiler);
2174         loadOptions(context, compiler);
2175 
2176         return compiler;
2177     }
2178 
2179     /**
2180      * Creates a FaceletFactory instance using the specified compiler.
2181      *
2182      * @param context
2183      *            the current FacesContext
2184      * @param compiler
2185      *            the compiler to be used by the factory
2186      *
2187      * @return the factory used by this VDL to load pages
2188      */
2189     protected FaceletFactory createFaceletFactory(FacesContext context, Compiler compiler)
2190     {
2191         ExternalContext eContext = context.getExternalContext();
2192 
2193         // refresh period
2194         long refreshPeriod;
2195         if (context.isProjectStage(ProjectStage.Production))
2196         {
2197             refreshPeriod = WebConfigParamUtils.getLongInitParameter(eContext, PARAMS_REFRESH_PERIOD,
2198                     DEFAULT_REFRESH_PERIOD_PRODUCTION);
2199         }
2200         else
2201         {
2202             refreshPeriod = WebConfigParamUtils.getLongInitParameter(eContext, PARAMS_REFRESH_PERIOD,
2203                     DEFAULT_REFRESH_PERIOD);
2204         }
2205 
2206         // resource resolver
2207         ResourceResolver resolver = new DefaultResourceResolver();
2208         ArrayList<String> classNames = new ArrayList<String>();
2209         String faceletsResourceResolverClassName = WebConfigParamUtils.getStringInitParameter(eContext,
2210                 PARAMS_RESOURCE_RESOLVER, null);
2211         List<String> resourceResolversFromAnnotations = RuntimeConfig.getCurrentInstance(
2212             context.getExternalContext()).getResourceResolvers();
2213         if (faceletsResourceResolverClassName != null)
2214         {
2215             classNames.add(faceletsResourceResolverClassName);
2216         }
2217         if (!resourceResolversFromAnnotations.isEmpty())
2218         {
2219             classNames.addAll(resourceResolversFromAnnotations);
2220         }
2221         if (!classNames.isEmpty())
2222         {
2223             resolver = ClassUtils.buildApplicationObject(ResourceResolver.class, classNames, resolver);
2224         }
2225 
2226         _resourceResolver = resolver;
2227 
2228         return new DefaultFaceletFactory(compiler, resolver, refreshPeriod);
2229     }
2230 
2231 
2232     protected ResponseWriter createResponseWriter(FacesContext context) throws IOException, FacesException
2233     {
2234         ExternalContext extContext = context.getExternalContext();
2235         RenderKit renderKit = context.getRenderKit();
2236         // Avoid a cryptic NullPointerException when the renderkit ID
2237         // is incorrectly set
2238         if (renderKit == null)
2239         {
2240             String id = context.getViewRoot().getRenderKitId();
2241             throw new IllegalStateException("No render kit was available for id \"" + id + "\"");
2242         }
2243 
2244         // set the buffer for content
2245         if (_bufferSize != -1)
2246         {
2247             extContext.setResponseBufferSize(_bufferSize);
2248         }
2249 
2250         // get our content type
2251         String contentType = (String) context.getAttributes().get("facelets.ContentType");
2252 
2253         // get the encoding
2254         String encoding = (String) context.getAttributes().get("facelets.Encoding");
2255 
2256         // -= Leonardo Uribe =- Add */* to the contentType is a fix done from FaceletViewHandler
2257         // to make old RI versions work, but since this is for JSF 2.0 it is not necessary that code.
2258         ResponseWriter writer = renderKit.createResponseWriter(FaceletsVDLUtils.NullWriter.INSTANCE,
2259             contentType, encoding);
2260 
2261         //ResponseWriter writer;
2262         // append */* to the contentType so createResponseWriter will succeed no matter
2263         // the requested contentType.
2264         //if (contentType != null && !contentType.equals("*/*"))
2265         //{
2266         //    contentType += ",*/*";
2267         //}
2268         // Create a dummy ResponseWriter with a bogus writer,
2269         // so we can figure out what content type the ReponseWriter
2270         // is really going to ask for
2271         //try
2272         //{
2273         //    writer = renderKit.createResponseWriter(NullWriter.Instance, contentType, encoding);
2274         //}
2275         // catch (IllegalArgumentException e)
2276         //{
2277         // Added because of an RI bug prior to 1.2_05-b3. Might as well leave it in case other
2278         // impls have the same problem. https://javaserverfaces.dev.java.net/issues/show_bug.cgi?id=613
2279         //log.finest("The impl didn't correctly handled '*/*' in the content type list.  Trying '*/*' directly.");
2280         //writer = renderKit.createResponseWriter(NullWriter.Instance, "*/*", encoding);
2281         //}
2282 
2283         // Override the JSF provided content type if necessary
2284         contentType = getResponseContentType(context, writer.getContentType());
2285         encoding = getResponseEncoding(context, writer.getCharacterEncoding());
2286 
2287         // apply them to the response
2288         extContext.setResponseContentType(contentType + "; charset=" + encoding);
2289 
2290         // removed 2005.8.23 to comply with J2EE 1.3
2291         // response.setCharacterEncoding(encoding);
2292 
2293         // Now, clone with the real writer
2294         writer = writer.cloneWithWriter(extContext.getResponseOutputWriter());
2295 
2296         return writer;
2297     }
2298 
2299     /**
2300      * @deprecated this code is not used anymore
2301      */
2302     @Deprecated
2303     protected String getDefaultSuffix(FacesContext context) throws FacesException
2304     {
2305         if (_defaultSuffix == null)
2306         {
2307             ExternalContext eContext = context.getExternalContext();
2308 
2309             String viewSuffix = eContext.getInitParameter(ViewHandler.DEFAULT_SUFFIX_PARAM_NAME);
2310 
2311             _defaultSuffix = viewSuffix == null ? ViewHandler.DEFAULT_FACELETS_SUFFIX : viewSuffix;
2312         }
2313 
2314         return _defaultSuffix;
2315     }
2316 
2317     /**
2318      * @deprecated
2319      */
2320     @Deprecated
2321     protected String getRenderedViewId(FacesContext context, String actionId)
2322     {
2323         // The previous code comes from Facelets 1.1.x but now it becomes invalid. In JSF 2.1, it is possible
2324         // to have multiple file extensions, and to make work correctly viewExists(), it is necessary to return
2325         // the viewId without changes
2326         return actionId;
2327     }
2328 
2329     /**
2330      * Generate the content type
2331      *
2332      * @param context
2333      * @param orig
2334      * @return
2335      */
2336     protected String getResponseContentType(FacesContext context, String orig)
2337     {
2338         String contentType = orig;
2339 
2340         // see if we need to override the contentType
2341         Map<Object, Object> m = context.getAttributes();
2342         if (m.containsKey("facelets.ContentType"))
2343         {
2344             contentType = (String) m.get("facelets.ContentType");
2345             if (log.isLoggable(Level.FINEST))
2346             {
2347                 log.finest("Facelet specified alternate contentType '" + contentType + "'");
2348             }
2349         }
2350 
2351         // safety check
2352         if (contentType == null)
2353         {
2354             contentType = "text/html";
2355             log.finest("ResponseWriter created had a null ContentType, defaulting to text/html");
2356         }
2357 
2358         return contentType;
2359     }
2360 
2361     /**
2362      * Generate the encoding
2363      *
2364      * @param context
2365      * @param orig
2366      * @return
2367      */
2368     protected String getResponseEncoding(FacesContext context, String orig)
2369     {
2370         String encoding = orig;
2371 
2372         // see if we need to override the encoding
2373         Map<Object, Object> m = context.getAttributes();
2374         Map<String, Object> sm = context.getExternalContext().getSessionMap();
2375 
2376         // 1. check the request attribute
2377         if (m.containsKey(PARAM_ENCODING))
2378         {
2379             encoding = (String) m.get(PARAM_ENCODING);
2380             if (encoding != null && log.isLoggable(Level.FINEST))
2381             {
2382                 log.finest("Facelet specified alternate encoding '" + encoding + "'");
2383             }
2384 
2385             sm.put(CHARACTER_ENCODING_KEY, encoding);
2386         }
2387 
2388         // 2. get it from request
2389         if (encoding == null)
2390         {
2391             encoding = context.getExternalContext().getRequestCharacterEncoding();
2392         }
2393 
2394         // 3. get it from the session
2395         if (encoding == null)
2396         {
2397             encoding = (String) sm.get(CHARACTER_ENCODING_KEY);
2398             if (encoding != null && log.isLoggable(Level.FINEST))
2399             {
2400                 log.finest("Session specified alternate encoding '" + encoding + "'");
2401             }
2402         }
2403 
2404         // 4. default it
2405         if (encoding == null)
2406         {
2407             encoding = DEFAULT_CHARACTER_ENCODING;
2408             if (log.isLoggable(Level.FINEST))
2409             {
2410                 log.finest("ResponseWriter created had a null CharacterEncoding, defaulting to " + encoding);
2411             }
2412         }
2413 
2414         return encoding;
2415     }
2416 
2417     protected void handleFaceletNotFound(FacesContext context, String viewId) throws FacesException, IOException
2418     {
2419         String actualId = context.getApplication().getViewHandler().getActionURL(context, viewId);
2420         context.getExternalContext().responseSendError(HttpServletResponse.SC_NOT_FOUND, actualId);
2421         context.responseComplete();
2422 
2423     }
2424 
2425     protected void handleRenderException(FacesContext context, Exception e)
2426             throws IOException, ELException, FacesException
2427     {
2428         /*
2429         UIViewRoot root = context.getViewRoot();
2430         StringBuffer sb = new StringBuffer(64);
2431         sb.append("Error Rendering View");
2432         if (root != null)
2433         {
2434             sb.append('[');
2435             sb.append(root.getViewId());
2436             sb.append(']');
2437         }
2438         
2439         log.log(Level.SEVERE, sb.toString(), e);
2440         */
2441 
2442         // rethrow the Exception to be handled by the ExceptionHandler
2443         if (e instanceof RuntimeException)
2444         {
2445             throw (RuntimeException) e;
2446         }
2447         else if (e instanceof IOException)
2448         {
2449             throw (IOException) e;
2450         }
2451         else
2452         {
2453             throw new FacesException(e.getMessage(), e);
2454         }
2455     }
2456 
2457     /**
2458      * Initialize the ViewHandler during its first request.
2459      */
2460     protected void initialize(FacesContext context)
2461     {
2462         log.finest("Initializing");
2463 
2464         Compiler compiler = createCompiler(context);
2465 
2466         _faceletFactory = createFaceletFactory(context, compiler);
2467 
2468         ExternalContext eContext = context.getExternalContext();
2469         _initializeBuffer(eContext);
2470         _initializeMode(eContext);
2471         _initializeContractMappings(eContext);
2472         
2473         // Create a component ids cache and store it on application map to
2474         // reduce the overhead associated with create such ids over and over.
2475         MyfacesConfig mfConfig = MyfacesConfig.getCurrentInstance(eContext);
2476         if (mfConfig.getComponentUniqueIdsCacheSize() > 0)
2477         {
2478             String[] componentIdsCached = SectionUniqueIdCounter.generateUniqueIdCache("_", 
2479                     mfConfig.getComponentUniqueIdsCacheSize());
2480             eContext.getApplicationMap().put(
2481                     CACHED_COMPONENT_IDS, componentIdsCached);
2482         }
2483         
2484         _viewPoolProcessor = ViewPoolProcessor.getInstance(context);
2485 
2486         log.finest("Initialization Successful");
2487     }
2488 
2489     /**
2490      * Load the various decorators for Facelets.
2491      *
2492      * @param context
2493      *            the current FacesContext
2494      * @param compiler
2495      *            the page compiler
2496      */
2497     protected void loadDecorators(FacesContext context, Compiler compiler)
2498     {
2499         getFaceletsCompilerSupport().loadDecorators(context, compiler);
2500     }
2501 
2502     protected FaceletsCompilerSupport getFaceletsCompilerSupport()
2503     {
2504         if (_faceletsCompilerSupport == null)
2505         {
2506             _faceletsCompilerSupport = new FaceletsCompilerSupport();
2507         }
2508         return _faceletsCompilerSupport;
2509     }
2510     
2511     public void setFaceletsCompilerSupport(FaceletsCompilerSupport support)
2512     {
2513         _faceletsCompilerSupport = support;
2514     }
2515 
2516     /**
2517      * Load the various tag libraries for Facelets.
2518      *
2519      * @param context
2520      *            the current FacesContext
2521      * @param compiler
2522      *            the page compiler
2523      */
2524     protected void loadLibraries(FacesContext context, Compiler compiler)
2525     {
2526         getFaceletsCompilerSupport().loadLibraries(context, compiler);
2527     }
2528 
2529     /**
2530      * Load the various options for Facelets compiler. Currently only comment skipping is supported.
2531      *
2532      * @param context
2533      *            the current FacesContext
2534      * @param compiler
2535      *            the page compiler
2536      */
2537     protected void loadOptions(FacesContext context, Compiler compiler)
2538     {
2539         getFaceletsCompilerSupport().loadOptions(context, compiler);
2540     }
2541 
2542     /**
2543      * {@inheritDoc}
2544      */
2545     @Override
2546     protected void sendSourceNotFound(FacesContext context, String message)
2547     {
2548         try
2549         {
2550             context.responseComplete();
2551             context.getExternalContext().responseSendError(HttpServletResponse.SC_NOT_FOUND, message);
2552         }
2553         catch (IOException ioe)
2554         {
2555             throw new FacesException(ioe);
2556         }
2557     }
2558 
2559     /**
2560      * Gets the Facelet representing the specified view identifier.
2561      *
2562      * @param viewId
2563      *            the view identifier
2564      *
2565      * @return the Facelet representing the specified view identifier
2566      *
2567      * @throws IOException
2568      *             if a read or parsing error occurs
2569      */
2570     private Facelet _getFacelet(FacesContext context, String viewId) throws IOException
2571     {
2572         // grab our FaceletFactory and create a Facelet
2573         FaceletFactory.setInstance(_faceletFactory);
2574         try
2575         {
2576             return _faceletFactory.getFacelet(context, viewId);
2577         }
2578         finally
2579         {
2580             FaceletFactory.setInstance(null);
2581         }
2582     }
2583 
2584     private Facelet _getViewMetadataFacelet(FacesContext context, String viewId) throws IOException
2585     {
2586         // grab our FaceletFactory and create a Facelet used to create view metadata
2587         FaceletFactory.setInstance(_faceletFactory);
2588         try
2589         {
2590             return _faceletFactory.getViewMetadataFacelet(context, viewId);
2591         }
2592         finally
2593         {
2594             FaceletFactory.setInstance(null);
2595         }
2596     }
2597 
2598     private void _initializeBuffer(ExternalContext context)
2599     {
2600         _bufferSize = WebConfigParamUtils.getIntegerInitParameter(context, PARAMS_BUFFER_SIZE, 1024);
2601     }
2602 
2603     private void _initializeMode(ExternalContext context)
2604     {
2605         String facesVersion = RuntimeConfig.getCurrentInstance(context).getFacesVersion();
2606         boolean partialStateSavingDefault;
2607 
2608         // Per spec section 11.1.3, the default value for the partial state saving feature needs
2609         // to be true if 2.0, false otherwise.
2610 
2611         partialStateSavingDefault = "2.0".equals(facesVersion) || "2.1".equals(facesVersion) || 
2612             "2.2".equals(facesVersion) || (facesVersion == null);
2613 
2614         // In jsf 2.0 this code evolve as PartialStateSaving feature
2615         //_buildBeforeRestore = _getBooleanParameter(context, PARAM_BUILD_BEFORE_RESTORE, false);
2616         _partialStateSaving = WebConfigParamUtils.getBooleanInitParameter(context,
2617                 StateManager.PARTIAL_STATE_SAVING_PARAM_NAME, partialStateSavingDefault);
2618 
2619         String[] viewIds = StringUtils.splitShortString(WebConfigParamUtils.getStringInitParameter(context,
2620                 StateManager.FULL_STATE_SAVING_VIEW_IDS_PARAM_NAME), ',');
2621 
2622         if (viewIds.length > 0)
2623         {
2624             _viewIds = new HashSet<String>(viewIds.length, 1.0f);
2625             Collections.addAll(_viewIds, viewIds);
2626         }
2627         else
2628         {
2629             _viewIds = null;
2630         }
2631 
2632         if (_partialStateSaving)
2633         {
2634             _refreshTransientBuildOnPSS = MyfacesConfig.getCurrentInstance(context).isRefreshTransientBuildOnPSS();
2635 
2636             _refreshTransientBuildOnPSSAuto
2637                     = MyfacesConfig.getCurrentInstance(context).isRefreshTransientBuildOnPSSAuto();
2638 
2639             _markInitialStateWhenApplyBuildView = WebConfigParamUtils.getBooleanInitParameter(context,
2640                     PARAM_MARK_INITIAL_STATE_WHEN_APPLY_BUILD_VIEW, false);
2641         }
2642     }
2643     
2644     private void _initializeContractMappings(ExternalContext context)
2645     {
2646         RuntimeConfig runtimeConfig = RuntimeConfig.getCurrentInstance(context);
2647         List<String> prefixWildcardKeys = new ArrayList<String>();
2648         Map<String, List<String>> contractMappings = new HashMap<String, List<String>>();
2649         
2650         for (Map.Entry<String, List<String>> entry : runtimeConfig.getContractMappings().entrySet())
2651         {
2652             String urlPattern = entry.getKey().trim();
2653             if (urlPattern.endsWith(ASTERISK))
2654             {
2655                 prefixWildcardKeys.add(urlPattern);
2656             }
2657             contractMappings.put(entry.getKey(), new ArrayList<String>(entry.getValue()));
2658         }
2659         
2660         Collections.sort(prefixWildcardKeys, new FaceletsVDLUtils.KeyComparator());
2661         
2662         this._prefixWildcardKeys = prefixWildcardKeys;
2663         this._contractMappings = contractMappings;
2664     }
2665 
2666     private boolean _usePartialStateSavingOnThisView(String viewId)
2667     {
2668         return _partialStateSaving && !(_viewIds != null && _viewIds.contains(viewId));
2669     }
2670 
2671     @Override
2672     public List<String> calculateResourceLibraryContracts(FacesContext context, String viewId)
2673     {
2674         List<String> contracts = this._contractMappings.get(viewId);
2675         if (contracts == null)
2676         {
2677             //Check prefix mapping
2678             for (String prefix : this._prefixWildcardKeys)
2679             {
2680                 if (FaceletsVDLUtils.matchPattern(viewId, prefix))
2681                 {
2682                     contracts =  this._contractMappings.get(prefix);
2683                     break;
2684                 }
2685             }
2686         }
2687         return contracts;
2688     }
2689 
2690     private class FaceletViewMetadata extends ViewMetadataBase
2691     {
2692         /**
2693          * Constructor
2694          *
2695          * Note that this viewId is not the one after calculateViewId() method
2696          */
2697         public FaceletViewMetadata(String viewId)
2698         {
2699             super(viewId);
2700         }
2701 
2702         @Override
2703         public UIViewRoot createMetadataView(FacesContext context)
2704         {
2705             try
2706             {
2707                 context.setProcessingEvents(false);
2708 
2709                 // spec doesn't say that this is necessary, but we blow up later if
2710                 // the viewroot isn't available from the FacesContext.
2711                 // -= Leonardo Uribe =- since it is supposed when we apply view metadata
2712                 // facelet we don't apply components with renderers and we don't call getRenderKit()
2713                 // it is safe to let this one commented
2714                 // context.setViewRoot(view);
2715 
2716                 // -= Leonardo Uribe =- This part is related to section 2.5.5 of jsf 2.0 spec.
2717                 // In theory what we need here is fill UIViewRoot.METADATA_FACET_NAME facet
2718                 // with UIViewParameter instances. Later, ViewHandlerImpl.getBookmarkableURL(),
2719                 // ViewHandlerImpl.getRedirectURL() and UIViewRoot.encodeEnd uses them. 
2720                 // For now, the only way to do this is call buildView(context,view) method, but 
2721                 // this is a waste of resources. We need to find another way to handle facelets view metadata.
2722                 // Call to buildView causes the view is not created on Render Response phase,
2723                 // if buildView is called from here all components pass through current lifecycle and only
2724                 // UIViewParameter instances should be taken into account.
2725                 // It should be an additional call to buildView on Render Response phase.
2726                 // buildView(context, view);
2727 
2728                 context.getAttributes().put(BUILDING_VIEW_METADATA, Boolean.TRUE);
2729 
2730                 // we have to invoke createView() on the application's ViewHandler
2731                 // here instead of invoking it directly in FaceletVDL, because
2732                 // the ViewHandler might be wrapped and wants to do some work
2733                 // in createView() (e.g. in Trinidad - see MYFACES-2641)
2734                 UIViewRoot view = context.getApplication().getViewHandler().createView(context, getViewId());
2735 
2736                 if (view != null)
2737                 {
2738                     // inside createView(context,viewId), calculateViewId() is called and
2739                     // the result is stored inside created UIViewRoot, so we can safely take the derived
2740                     // viewId from there.
2741                     Facelet facelet = null;
2742                     try
2743                     {
2744                         facelet = _getViewMetadataFacelet(context, view.getViewId());
2745                     }
2746                     catch (FileNotFoundException e)
2747                     {
2748                         sendSourceNotFound(context, getViewId());
2749                         return null;
2750                     }
2751 
2752                     facelet.apply(context, view);
2753                 }
2754 
2755                 return view;
2756             }
2757             catch (IOException ioe)
2758             {
2759                 throw new FacesException(ioe);
2760             }
2761             finally
2762             {
2763                 context.getAttributes().remove(BUILDING_VIEW_METADATA);
2764 
2765                 context.setProcessingEvents(true);
2766             }
2767         }
2768     }
2769     
2770     public FaceletFactory getFaceletFactory()
2771     {
2772         return _faceletFactory;
2773     }
2774 
2775     @Override
2776     public UIComponent createComponent(FacesContext context, 
2777         String taglibURI, String tagName, Map<String, Object> attributes)
2778     {
2779         checkNull(context, "context");
2780         UIComponent createdComponent = null;
2781         try
2782         {
2783             Facelet componentFacelet;
2784             FaceletFactory.setInstance(_faceletFactory);
2785             try
2786             {
2787                 componentFacelet
2788                         = _faceletFactory.compileComponentFacelet(taglibURI, tagName, attributes);
2789             }
2790             finally
2791             {
2792                 FaceletFactory.setInstance(null);
2793             }
2794             if (componentFacelet == null)
2795             {
2796                 return null;
2797             }
2798             // Create a temporal component base class where all components will be put, but we are only
2799             // interested in the inner UIComponent and if multiple are created, return this one.
2800             boolean requiresDynamicRefresh = false;
2801             boolean requiresFaceletDynamicRefresh = false;
2802             UIPanel tempParent
2803                     = (UIPanel) context.getApplication().createComponent(
2804                     context, UIPanel.COMPONENT_TYPE, null);
2805             tempParent.setId(context.getViewRoot().createUniqueId(context, null));
2806             String baseKey = tempParent.getId();
2807             baseKey = baseKey.startsWith(UIViewRoot.UNIQUE_ID_PREFIX) ? baseKey.substring(4) : baseKey;
2808 
2809             try
2810             {
2811                 tempParent.pushComponentToEL(context, tempParent);
2812                 ((AbstractFacelet)componentFacelet).applyDynamicComponentHandler(
2813                     context, tempParent, baseKey);
2814             }
2815             finally
2816             {
2817                 tempParent.popComponentFromEL(context);
2818                 // There are two cases:
2819                 // 1. If we are under facelets algorithm control (binding case), the refreshing logic will be done
2820                 // outside this block. We can check that condition easily with FaceletCompositionContext
2821                 // 2. If we are not under facelets algorithm control, check if the dynamic component requires refresh,
2822                 // if that so, mark the view to be refreshed and reset the flag, otherwise continue. This check
2823                 // allows us to decide if we add a third listener to refresh on transient build.
2824                     // Check if the current component requires dynamic refresh and if that so,
2825                 FaceletCompositionContext fcc = FaceletCompositionContext.getCurrentInstance(context);
2826                 if (fcc != null)
2827                 {
2828                     requiresFaceletDynamicRefresh = true;
2829                 }
2830                 else if (FaceletViewDeclarationLanguageBase.isDynamicComponentNeedsRefresh(context))
2831                 {
2832                     FaceletViewDeclarationLanguageBase.activateDynamicComponentRefreshTransientBuild(context);
2833                     FaceletViewDeclarationLanguageBase.resetDynamicComponentNeedsRefreshFlag(context);
2834                     requiresDynamicRefresh = true;
2835                 }
2836             }
2837             if (tempParent.getChildCount() > 1)
2838             {
2839                 // Multiple child. The tempParent will be returned. No need to
2840                 // save MARK_CREATED.
2841                 createdComponent = tempParent;
2842                 tempParent.getAttributes().put("oam.vf.DYN_WRAPPER", baseKey);
2843                 tempParent.subscribeToEvent(PostRestoreStateEvent.class, new 
2844                     RefreshDynamicComponentListener(taglibURI, tagName, attributes, baseKey));
2845                 if (requiresFaceletDynamicRefresh)
2846                 {
2847                     FaceletViewDeclarationLanguageBase.dynamicComponentNeedsRefresh(context);
2848                 }
2849             }
2850             else if (tempParent.getChildCount() == 1)
2851             {
2852                 createdComponent = tempParent.getChildren().get(0);
2853                 boolean requiresRefresh = false;
2854                 // One child. In that case there are three choices:
2855                 if (UIComponent.isCompositeComponent(createdComponent))
2856                 {
2857                     // 1. Composite component. Needs special handling because
2858                     // facets will be added programatically. The algorithm that
2859                     // process the composite component content should occur
2860                     // after the component is added to the view (PostAddToViewEvent).
2861                     // Requires refresh. To do that, we need to save the MARK_CREATED
2862                     // value and set it only when the full component is refreshed after 
2863                     // restore it.
2864                     createdComponent.getAttributes().put("oam.vf.GEN_MARK_ID",
2865                         createdComponent.getAttributes().get(ComponentSupport.MARK_CREATED));
2866                     createdComponent.getAttributes().put(ComponentSupport.MARK_CREATED, null);
2867                     createdComponent.subscribeToEvent(PostAddToViewEvent.class, new 
2868                         CreateDynamicCompositeComponentListener(taglibURI, tagName, attributes, baseKey));
2869                     requiresRefresh = true;
2870                     if (requiresFaceletDynamicRefresh)
2871                     {
2872                         FaceletViewDeclarationLanguageBase.dynamicComponentNeedsRefresh(context);
2873                     }
2874                 }
2875                 else if (createdComponent.getChildCount() > 0)
2876                 {
2877                     // 2. Single component with children inside.
2878                     // Requires refresh. To do that, we need to save the MARK_CREATED
2879                     // value and set it only when the full component is refreshed after 
2880                     // restore it.
2881                     createdComponent.getAttributes().put("oam.vf.GEN_MARK_ID",
2882                         createdComponent.getAttributes().get(ComponentSupport.MARK_CREATED));
2883                     createdComponent.getAttributes().put(ComponentSupport.MARK_CREATED, null);
2884                     requiresRefresh = true;
2885                     if (requiresFaceletDynamicRefresh)
2886                     {
2887                         FaceletViewDeclarationLanguageBase.dynamicComponentNeedsRefresh(context);
2888                     }
2889                 }
2890                 else if (createdComponent.isTransient())
2891                 {
2892                     // Just transient markup inside. It is necessary to wrap
2893                     // that content into a component. Requires refresh. No need to
2894                     // save MARK_CREATED. No requires dynamic refresh.
2895                     createdComponent = tempParent;
2896                     tempParent.getAttributes().put("oam.vf.DYN_WRAPPER", baseKey);
2897                     requiresRefresh = true;
2898                 }
2899                 else
2900                 {
2901                     // 4. Single component without children: 
2902                     // Remove MARK_CREATED because it does not requires
2903                     // refresh on restore. When it is added to the component
2904                     // tree, it will be saved and restored as if was a programatically
2905                     // added component.
2906                     createdComponent.getAttributes().put(ComponentSupport.MARK_CREATED, null);
2907                 }
2908                 if (requiresRefresh)
2909                 {
2910                     createdComponent.subscribeToEvent(PostRestoreStateEvent.class, new 
2911                         RefreshDynamicComponentListener(taglibURI, tagName, attributes, baseKey));
2912                 }
2913                 if (requiresDynamicRefresh)
2914                 {
2915                     createdComponent.subscribeToEvent(DynamicComponentRefreshTransientBuildEvent.class, new 
2916                         RefreshDynamicComponentListener(taglibURI, tagName, attributes, baseKey));
2917                     createdComponent.getAttributes().put(
2918                             DynamicComponentRefreshTransientBuildEvent.DYN_COMP_REFRESH_FLAG, Boolean.TRUE);
2919                 }
2920                 if (requiresFaceletDynamicRefresh)
2921                 {
2922                     createdComponent.subscribeToEvent(FaceletDynamicComponentRefreshTransientBuildEvent.class, new 
2923                         RefreshDynamicComponentListener(taglibURI, tagName, attributes, baseKey));
2924                 }
2925             }
2926         }
2927         catch (IOException e)
2928         {
2929             throw new FacesException(e);
2930         }
2931         return createdComponent;
2932     }
2933     
2934     protected RenderKitFactory getRenderKitFactory()
2935     {
2936         if (_renderKitFactory == null)
2937         {
2938             _renderKitFactory = (RenderKitFactory)FactoryFinder.getFactory(FactoryFinder.RENDER_KIT_FACTORY);
2939         }
2940         return _renderKitFactory;
2941     }
2942 }