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: 1552960 $ $Date: 2013-12-21 19:34:47 -0500 (Sat, 21 Dec 2013) $
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 = 2;
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 (stateObj == null)
1927                             {
1928                                 stateStr = null;
1929                             }
1930                             else
1931                             {
1932                                 stateMgr.writeState(context, stateObj);
1933                                 stateStr = stateWriter.getAndResetBuffer();
1934                             }
1935 
1936                             int start = 0;
1937 
1938                             while (end != -1)
1939                             {
1940                                 origWriter.write(content, start, end - start);
1941                                 if (stateStr != null)
1942                                 {
1943                                     origWriter.write(stateStr);
1944                                 }
1945                                 start = end + STATE_KEY_LEN;
1946                                 end = content.indexOf(STATE_KEY, start);
1947                             }
1948 
1949                             origWriter.write(content, start, content.length() - start);
1950                             // No trace of any saved state, so we just need to flush
1951                             // the buffer
1952                         }
1953                         else
1954                         {
1955                             origWriter.write(content);
1956                         }
1957                     }
1958                     else if (stateWriter.isStateWrittenWithoutWrapper())
1959                     {
1960                         // The state token has been written but the state has not been
1961                         // saved yet.
1962                         stateMgr.saveView(context);
1963                     }
1964                     else
1965                     {
1966                         // GET case without any form that trigger state saving.
1967                         // Try to store it into cache.
1968                         if (_viewPoolProcessor != null && 
1969                             _viewPoolProcessor.isViewPoolEnabledForThisView(context, view))
1970                         {
1971                             ViewDeclarationLanguage vdl = context.getApplication().
1972                                     getViewHandler().getViewDeclarationLanguage(
1973                                         context, view.getViewId());
1974 
1975                             if (ViewDeclarationLanguage.FACELETS_VIEW_DECLARATION_LANGUAGE_ID.equals(
1976                                     vdl.getId()))
1977                             {
1978                                 StateManagementStrategy sms = vdl.getStateManagementStrategy(
1979                                         context, view.getId());
1980                                 if (sms != null)
1981                                 {
1982                                     context.getAttributes().put(ViewPoolProcessor.FORCE_HARD_RESET, Boolean.TRUE);
1983 
1984                                     // Force indirectly to store the map in the cache
1985                                     try
1986                                     {
1987                                         Object state = sms.saveView(context);
1988                                     }
1989                                     finally
1990                                     {
1991                                         context.getAttributes().remove(ViewPoolProcessor.FORCE_HARD_RESET);
1992                                     }
1993 
1994                                     // Clear the calculated value from the application map
1995                                     context.getAttributes().remove(SERIALIZED_VIEW_REQUEST_ATTR);
1996                                 }
1997                             }
1998                         }
1999                     }
2000                 }
2001                 finally
2002                 {
2003                     // The Facelets implementation must close the writer used to write the response
2004                     writer.close();
2005                 }
2006             }
2007             finally
2008             {
2009                 stateWriter.release(context);
2010             }
2011         }
2012         catch (FileNotFoundException fnfe)
2013         {
2014             handleFaceletNotFound(context, view.getViewId());
2015         }
2016         catch (Exception e)
2017         {
2018             handleRenderException(context, e);
2019         }
2020     }
2021     
2022     private static final String SERIALIZED_VIEW_REQUEST_ATTR = 
2023         StateManagerImpl.class.getName() + ".SERIALIZED_VIEW";
2024 
2025     /**
2026      * {@inheritDoc}
2027      */
2028     @Override
2029     public UIViewRoot createView(FacesContext context, String viewId)
2030     {
2031         checkNull(viewId, "viewId");
2032         // we have to check for a possible debug request
2033         if (UIDebug.debugRequest(context))
2034         {
2035             // the current request is a debug request, so we don't need
2036             // to create a view, since the output has already been written
2037             // in UIDebug.debugRequest() and facesContext.responseComplete()
2038             // has been called.
2039             return null;
2040         }
2041         else
2042         {
2043             UIViewRoot root = super.createView(context, viewId);
2044             if (root != null)
2045             {
2046                 //Ensure calculateResourceLibraryContracts() can be decorated
2047                 ViewDeclarationLanguage vdl = context.getApplication().getViewHandler().
2048                     getViewDeclarationLanguage(context, viewId);
2049                 List<String> contracts = vdl.calculateResourceLibraryContracts(
2050                     context, root.getViewId() != null ? root.getViewId() : viewId);
2051                 context.setResourceLibraryContracts(contracts);
2052             }
2053             return root;
2054         }
2055     }
2056 
2057     /**
2058      * {@inheritDoc}
2059      */
2060     @Override
2061     public UIViewRoot restoreView(FacesContext context, String viewId)
2062     {
2063         checkNull(viewId, "viewId");
2064         // Currently there is no way, in which UIDebug.debugRequest(context)
2065         // can create debug information and return true at this point,
2066         // because this method is only accessed if the current request
2067         // is a postback, which will never be true for a debug page.
2068         // The only point where valid debug output can be produced by now
2069         // is in createView() -= Jakob Korherr =-
2070         //if (UIDebug.debugRequest(context))
2071         //{
2072         //    return new UIViewRoot();
2073         //}
2074 
2075         //else if (!_buildBeforeRestore)
2076         //{
2077         
2078         // When partial state saving is not used, createView() is not called. To ensure
2079         // everything is in place it is necessary to calculate the resource library 
2080         // contracts and set them into facesContext.
2081         ViewDeclarationLanguage vdl = context.getApplication().getViewHandler().
2082             getViewDeclarationLanguage(context, viewId);
2083         List<String> contracts = vdl.calculateResourceLibraryContracts(
2084             context, viewId);
2085         context.setResourceLibraryContracts(contracts);
2086         
2087         // JSF 2.2 stateless views
2088         // We need to check if the incoming view is stateless or not and if that so rebuild it here
2089         // note we cannot do this in DefaultFaceletsStateManagementStrategy because it is only used
2090         // when PSS is enabled, but stateless views can be used without PSS. If the view is stateless,
2091         // there is no need to ask to the StateManager.
2092         Application application = context.getApplication();
2093         ViewHandler applicationViewHandler = application.getViewHandler();
2094         String renderKitId = applicationViewHandler.calculateRenderKitId(context);
2095 
2096         ResponseStateManager manager = getRenderKitFactory().getRenderKit(
2097             context, renderKitId).getResponseStateManager();
2098         
2099         if (manager.isStateless(context, viewId))
2100         {
2101             // Per the spec: build the view.
2102             UIViewRoot view = null;
2103             try
2104             {
2105                 ViewMetadata metadata = vdl.getViewMetadata (context, viewId);
2106                 if (metadata != null)
2107                 {
2108                     view = metadata.createMetadataView(context);
2109                 }
2110                 if (view == null)
2111                 {
2112                     view = context.getApplication().getViewHandler().createView(context, viewId);
2113                 }
2114                 context.setViewRoot (view); 
2115                 boolean oldContextEventState = context.isProcessingEvents();
2116                 try 
2117                 {
2118                     context.setProcessingEvents (true);
2119                     vdl.buildView (context, view);
2120                 }
2121                 finally
2122                 {
2123                     context.setProcessingEvents (oldContextEventState);
2124                 } 
2125             }
2126             catch (Throwable e)
2127             {
2128                 throw new FacesException ("unable to create view \"" + viewId + "\"", e);
2129             }
2130             return view;
2131         }
2132         else
2133         {
2134             return super.restoreView(context, viewId);
2135         }
2136     }
2137 
2138     /**
2139      * {@inheritDoc}
2140      */
2141     @Override
2142     protected String calculateViewId(FacesContext context, String viewId)
2143     {
2144         if (_cachedViewHandlerSupport == null)
2145         {
2146             _cachedViewHandlerSupport = new DefaultViewHandlerSupport();
2147         }
2148 
2149         return _cachedViewHandlerSupport.calculateViewId(context, viewId);
2150     }
2151 
2152     /**
2153      * Creates the Facelet page compiler.
2154      *
2155      * @param context
2156      *            the current FacesContext
2157      *
2158      * @return the application's Facelet page compiler
2159      */
2160     protected Compiler createCompiler(FacesContext context)
2161     {
2162         Compiler compiler = new SAXCompiler();
2163 
2164         compiler.setDevelopmentProjectStage(context.isProjectStage(ProjectStage.Development));
2165 
2166         loadLibraries(context, compiler);
2167         loadDecorators(context, compiler);
2168         loadOptions(context, compiler);
2169 
2170         return compiler;
2171     }
2172 
2173     /**
2174      * Creates a FaceletFactory instance using the specified compiler.
2175      *
2176      * @param context
2177      *            the current FacesContext
2178      * @param compiler
2179      *            the compiler to be used by the factory
2180      *
2181      * @return the factory used by this VDL to load pages
2182      */
2183     protected FaceletFactory createFaceletFactory(FacesContext context, Compiler compiler)
2184     {
2185         ExternalContext eContext = context.getExternalContext();
2186 
2187         // refresh period
2188         long refreshPeriod;
2189         if (context.isProjectStage(ProjectStage.Production))
2190         {
2191             refreshPeriod = WebConfigParamUtils.getLongInitParameter(eContext, PARAMS_REFRESH_PERIOD,
2192                     DEFAULT_REFRESH_PERIOD_PRODUCTION);
2193         }
2194         else
2195         {
2196             refreshPeriod = WebConfigParamUtils.getLongInitParameter(eContext, PARAMS_REFRESH_PERIOD,
2197                     DEFAULT_REFRESH_PERIOD);
2198         }
2199 
2200         // resource resolver
2201         ResourceResolver resolver = new DefaultResourceResolver();
2202         ArrayList<String> classNames = new ArrayList<String>();
2203         String faceletsResourceResolverClassName = WebConfigParamUtils.getStringInitParameter(eContext,
2204                 PARAMS_RESOURCE_RESOLVER, null);
2205         List<String> resourceResolversFromAnnotations = RuntimeConfig.getCurrentInstance(
2206             context.getExternalContext()).getResourceResolvers();
2207         if (faceletsResourceResolverClassName != null)
2208         {
2209             classNames.add(faceletsResourceResolverClassName);
2210         }
2211         if (!resourceResolversFromAnnotations.isEmpty())
2212         {
2213             classNames.addAll(resourceResolversFromAnnotations);
2214         }
2215         if (!classNames.isEmpty())
2216         {
2217             resolver = ClassUtils.buildApplicationObject(ResourceResolver.class, classNames, resolver);
2218         }
2219 
2220         _resourceResolver = resolver;
2221 
2222         return new DefaultFaceletFactory(compiler, resolver, refreshPeriod);
2223     }
2224 
2225 
2226     protected ResponseWriter createResponseWriter(FacesContext context) throws IOException, FacesException
2227     {
2228         ExternalContext extContext = context.getExternalContext();
2229         RenderKit renderKit = context.getRenderKit();
2230         // Avoid a cryptic NullPointerException when the renderkit ID
2231         // is incorrectly set
2232         if (renderKit == null)
2233         {
2234             String id = context.getViewRoot().getRenderKitId();
2235             throw new IllegalStateException("No render kit was available for id \"" + id + "\"");
2236         }
2237 
2238         // set the buffer for content
2239         if (_bufferSize != -1)
2240         {
2241             extContext.setResponseBufferSize(_bufferSize);
2242         }
2243 
2244         // get our content type
2245         String contentType = (String) context.getAttributes().get("facelets.ContentType");
2246 
2247         // get the encoding
2248         String encoding = (String) context.getAttributes().get("facelets.Encoding");
2249 
2250         // -= Leonardo Uribe =- Add */* to the contentType is a fix done from FaceletViewHandler
2251         // to make old RI versions work, but since this is for JSF 2.0 it is not necessary that code.
2252         ResponseWriter writer = renderKit.createResponseWriter(FaceletsVDLUtils.NullWriter.INSTANCE,
2253             contentType, encoding);
2254 
2255         //ResponseWriter writer;
2256         // append */* to the contentType so createResponseWriter will succeed no matter
2257         // the requested contentType.
2258         //if (contentType != null && !contentType.equals("*/*"))
2259         //{
2260         //    contentType += ",*/*";
2261         //}
2262         // Create a dummy ResponseWriter with a bogus writer,
2263         // so we can figure out what content type the ReponseWriter
2264         // is really going to ask for
2265         //try
2266         //{
2267         //    writer = renderKit.createResponseWriter(NullWriter.Instance, contentType, encoding);
2268         //}
2269         // catch (IllegalArgumentException e)
2270         //{
2271         // Added because of an RI bug prior to 1.2_05-b3. Might as well leave it in case other
2272         // impls have the same problem. https://javaserverfaces.dev.java.net/issues/show_bug.cgi?id=613
2273         //log.finest("The impl didn't correctly handled '*/*' in the content type list.  Trying '*/*' directly.");
2274         //writer = renderKit.createResponseWriter(NullWriter.Instance, "*/*", encoding);
2275         //}
2276 
2277         // Override the JSF provided content type if necessary
2278         contentType = getResponseContentType(context, writer.getContentType());
2279         encoding = getResponseEncoding(context, writer.getCharacterEncoding());
2280 
2281         // apply them to the response
2282         extContext.setResponseContentType(contentType + "; charset=" + encoding);
2283 
2284         // removed 2005.8.23 to comply with J2EE 1.3
2285         // response.setCharacterEncoding(encoding);
2286 
2287         // Now, clone with the real writer
2288         writer = writer.cloneWithWriter(extContext.getResponseOutputWriter());
2289 
2290         return writer;
2291     }
2292 
2293     /**
2294      * @deprecated this code is not used anymore
2295      */
2296     @Deprecated
2297     protected String getDefaultSuffix(FacesContext context) throws FacesException
2298     {
2299         if (_defaultSuffix == null)
2300         {
2301             ExternalContext eContext = context.getExternalContext();
2302 
2303             String viewSuffix = eContext.getInitParameter(ViewHandler.DEFAULT_SUFFIX_PARAM_NAME);
2304 
2305             _defaultSuffix = viewSuffix == null ? ViewHandler.DEFAULT_FACELETS_SUFFIX : viewSuffix;
2306         }
2307 
2308         return _defaultSuffix;
2309     }
2310 
2311     /**
2312      * @deprecated
2313      */
2314     @Deprecated
2315     protected String getRenderedViewId(FacesContext context, String actionId)
2316     {
2317         // The previous code comes from Facelets 1.1.x but now it becomes invalid. In JSF 2.1, it is possible
2318         // to have multiple file extensions, and to make work correctly viewExists(), it is necessary to return
2319         // the viewId without changes
2320         return actionId;
2321     }
2322 
2323     /**
2324      * Generate the content type
2325      *
2326      * @param context
2327      * @param orig
2328      * @return
2329      */
2330     protected String getResponseContentType(FacesContext context, String orig)
2331     {
2332         String contentType = orig;
2333 
2334         // see if we need to override the contentType
2335         Map<Object, Object> m = context.getAttributes();
2336         if (m.containsKey("facelets.ContentType"))
2337         {
2338             contentType = (String) m.get("facelets.ContentType");
2339             if (log.isLoggable(Level.FINEST))
2340             {
2341                 log.finest("Facelet specified alternate contentType '" + contentType + "'");
2342             }
2343         }
2344 
2345         // safety check
2346         if (contentType == null)
2347         {
2348             contentType = "text/html";
2349             log.finest("ResponseWriter created had a null ContentType, defaulting to text/html");
2350         }
2351 
2352         return contentType;
2353     }
2354 
2355     /**
2356      * Generate the encoding
2357      *
2358      * @param context
2359      * @param orig
2360      * @return
2361      */
2362     protected String getResponseEncoding(FacesContext context, String orig)
2363     {
2364         String encoding = orig;
2365 
2366         // see if we need to override the encoding
2367         Map<Object, Object> m = context.getAttributes();
2368         Map<String, Object> sm = context.getExternalContext().getSessionMap();
2369 
2370         // 1. check the request attribute
2371         if (m.containsKey(PARAM_ENCODING))
2372         {
2373             encoding = (String) m.get(PARAM_ENCODING);
2374             if (encoding != null && log.isLoggable(Level.FINEST))
2375             {
2376                 log.finest("Facelet specified alternate encoding '" + encoding + "'");
2377             }
2378 
2379             sm.put(CHARACTER_ENCODING_KEY, encoding);
2380         }
2381 
2382         // 2. get it from request
2383         if (encoding == null)
2384         {
2385             encoding = context.getExternalContext().getRequestCharacterEncoding();
2386         }
2387 
2388         // 3. get it from the session
2389         if (encoding == null)
2390         {
2391             encoding = (String) sm.get(CHARACTER_ENCODING_KEY);
2392             if (encoding != null && log.isLoggable(Level.FINEST))
2393             {
2394                 log.finest("Session specified alternate encoding '" + encoding + "'");
2395             }
2396         }
2397 
2398         // 4. default it
2399         if (encoding == null)
2400         {
2401             encoding = DEFAULT_CHARACTER_ENCODING;
2402             if (log.isLoggable(Level.FINEST))
2403             {
2404                 log.finest("ResponseWriter created had a null CharacterEncoding, defaulting to " + encoding);
2405             }
2406         }
2407 
2408         return encoding;
2409     }
2410 
2411     protected void handleFaceletNotFound(FacesContext context, String viewId) throws FacesException, IOException
2412     {
2413         String actualId = context.getApplication().getViewHandler().getActionURL(context, viewId);
2414         context.getExternalContext().responseSendError(HttpServletResponse.SC_NOT_FOUND, actualId);
2415         context.responseComplete();
2416 
2417     }
2418 
2419     protected void handleRenderException(FacesContext context, Exception e)
2420             throws IOException, ELException, FacesException
2421     {
2422         /*
2423         UIViewRoot root = context.getViewRoot();
2424         StringBuffer sb = new StringBuffer(64);
2425         sb.append("Error Rendering View");
2426         if (root != null)
2427         {
2428             sb.append('[');
2429             sb.append(root.getViewId());
2430             sb.append(']');
2431         }
2432         
2433         log.log(Level.SEVERE, sb.toString(), e);
2434         */
2435 
2436         // rethrow the Exception to be handled by the ExceptionHandler
2437         if (e instanceof RuntimeException)
2438         {
2439             throw (RuntimeException) e;
2440         }
2441         else if (e instanceof IOException)
2442         {
2443             throw (IOException) e;
2444         }
2445         else
2446         {
2447             throw new FacesException(e.getMessage(), e);
2448         }
2449     }
2450 
2451     /**
2452      * Initialize the ViewHandler during its first request.
2453      */
2454     protected void initialize(FacesContext context)
2455     {
2456         log.finest("Initializing");
2457 
2458         Compiler compiler = createCompiler(context);
2459 
2460         _faceletFactory = createFaceletFactory(context, compiler);
2461 
2462         ExternalContext eContext = context.getExternalContext();
2463         _initializeBuffer(eContext);
2464         _initializeMode(eContext);
2465         _initializeContractMappings(eContext);
2466         
2467         // Create a component ids cache and store it on application map to
2468         // reduce the overhead associated with create such ids over and over.
2469         MyfacesConfig mfConfig = MyfacesConfig.getCurrentInstance(eContext);
2470         if (mfConfig.getComponentUniqueIdsCacheSize() > 0)
2471         {
2472             String[] componentIdsCached = SectionUniqueIdCounter.generateUniqueIdCache("_", 
2473                     mfConfig.getComponentUniqueIdsCacheSize());
2474             eContext.getApplicationMap().put(
2475                     CACHED_COMPONENT_IDS, componentIdsCached);
2476         }
2477         
2478         _viewPoolProcessor = ViewPoolProcessor.getInstance(context);
2479 
2480         log.finest("Initialization Successful");
2481     }
2482 
2483     /**
2484      * Load the various decorators for Facelets.
2485      *
2486      * @param context
2487      *            the current FacesContext
2488      * @param compiler
2489      *            the page compiler
2490      */
2491     protected void loadDecorators(FacesContext context, Compiler compiler)
2492     {
2493         getFaceletsCompilerSupport().loadDecorators(context, compiler);
2494     }
2495 
2496     protected FaceletsCompilerSupport getFaceletsCompilerSupport()
2497     {
2498         if (_faceletsCompilerSupport == null)
2499         {
2500             _faceletsCompilerSupport = new FaceletsCompilerSupport();
2501         }
2502         return _faceletsCompilerSupport;
2503     }
2504     
2505     public void setFaceletsCompilerSupport(FaceletsCompilerSupport support)
2506     {
2507         _faceletsCompilerSupport = support;
2508     }
2509 
2510     /**
2511      * Load the various tag libraries for Facelets.
2512      *
2513      * @param context
2514      *            the current FacesContext
2515      * @param compiler
2516      *            the page compiler
2517      */
2518     protected void loadLibraries(FacesContext context, Compiler compiler)
2519     {
2520         getFaceletsCompilerSupport().loadLibraries(context, compiler);
2521     }
2522 
2523     /**
2524      * Load the various options for Facelets compiler. Currently only comment skipping is supported.
2525      *
2526      * @param context
2527      *            the current FacesContext
2528      * @param compiler
2529      *            the page compiler
2530      */
2531     protected void loadOptions(FacesContext context, Compiler compiler)
2532     {
2533         getFaceletsCompilerSupport().loadOptions(context, compiler);
2534     }
2535 
2536     /**
2537      * {@inheritDoc}
2538      */
2539     @Override
2540     protected void sendSourceNotFound(FacesContext context, String message)
2541     {
2542         try
2543         {
2544             context.responseComplete();
2545             context.getExternalContext().responseSendError(HttpServletResponse.SC_NOT_FOUND, message);
2546         }
2547         catch (IOException ioe)
2548         {
2549             throw new FacesException(ioe);
2550         }
2551     }
2552 
2553     /**
2554      * Gets the Facelet representing the specified view identifier.
2555      *
2556      * @param viewId
2557      *            the view identifier
2558      *
2559      * @return the Facelet representing the specified view identifier
2560      *
2561      * @throws IOException
2562      *             if a read or parsing error occurs
2563      */
2564     private Facelet _getFacelet(FacesContext context, String viewId) throws IOException
2565     {
2566         // grab our FaceletFactory and create a Facelet
2567         FaceletFactory.setInstance(_faceletFactory);
2568         try
2569         {
2570             return _faceletFactory.getFacelet(context, viewId);
2571         }
2572         finally
2573         {
2574             FaceletFactory.setInstance(null);
2575         }
2576     }
2577 
2578     private Facelet _getViewMetadataFacelet(FacesContext context, String viewId) throws IOException
2579     {
2580         // grab our FaceletFactory and create a Facelet used to create view metadata
2581         FaceletFactory.setInstance(_faceletFactory);
2582         try
2583         {
2584             return _faceletFactory.getViewMetadataFacelet(context, viewId);
2585         }
2586         finally
2587         {
2588             FaceletFactory.setInstance(null);
2589         }
2590     }
2591 
2592     private void _initializeBuffer(ExternalContext context)
2593     {
2594         _bufferSize = WebConfigParamUtils.getIntegerInitParameter(context, PARAMS_BUFFER_SIZE, 1024);
2595     }
2596 
2597     private void _initializeMode(ExternalContext context)
2598     {
2599         String facesVersion = RuntimeConfig.getCurrentInstance(context).getFacesVersion();
2600         boolean partialStateSavingDefault;
2601 
2602         // Per spec section 11.1.3, the default value for the partial state saving feature needs
2603         // to be true if 2.0, false otherwise.
2604 
2605         partialStateSavingDefault = "2.0".equals(facesVersion) || "2.1".equals(facesVersion) || 
2606             "2.2".equals(facesVersion) || (facesVersion == null);
2607 
2608         // In jsf 2.0 this code evolve as PartialStateSaving feature
2609         //_buildBeforeRestore = _getBooleanParameter(context, PARAM_BUILD_BEFORE_RESTORE, false);
2610         _partialStateSaving = WebConfigParamUtils.getBooleanInitParameter(context,
2611                 StateManager.PARTIAL_STATE_SAVING_PARAM_NAME, partialStateSavingDefault);
2612 
2613         String[] viewIds = StringUtils.splitShortString(WebConfigParamUtils.getStringInitParameter(context,
2614                 StateManager.FULL_STATE_SAVING_VIEW_IDS_PARAM_NAME), ',');
2615 
2616         if (viewIds.length > 0)
2617         {
2618             _viewIds = new HashSet<String>(viewIds.length, 1.0f);
2619             Collections.addAll(_viewIds, viewIds);
2620         }
2621         else
2622         {
2623             _viewIds = null;
2624         }
2625 
2626         if (_partialStateSaving)
2627         {
2628             _refreshTransientBuildOnPSS = MyfacesConfig.getCurrentInstance(context).isRefreshTransientBuildOnPSS();
2629 
2630             _refreshTransientBuildOnPSSAuto
2631                     = MyfacesConfig.getCurrentInstance(context).isRefreshTransientBuildOnPSSAuto();
2632 
2633             _markInitialStateWhenApplyBuildView = WebConfigParamUtils.getBooleanInitParameter(context,
2634                     PARAM_MARK_INITIAL_STATE_WHEN_APPLY_BUILD_VIEW, false);
2635         }
2636     }
2637     
2638     private void _initializeContractMappings(ExternalContext context)
2639     {
2640         RuntimeConfig runtimeConfig = RuntimeConfig.getCurrentInstance(context);
2641         List<String> prefixWildcardKeys = new ArrayList<String>();
2642         Map<String, List<String>> contractMappings = new HashMap<String, List<String>>();
2643         
2644         for (Map.Entry<String, List<String>> entry : runtimeConfig.getContractMappings().entrySet())
2645         {
2646             String urlPattern = entry.getKey().trim();
2647             if (urlPattern.endsWith(ASTERISK))
2648             {
2649                 prefixWildcardKeys.add(urlPattern);
2650             }
2651             contractMappings.put(entry.getKey(), new ArrayList<String>(entry.getValue()));
2652         }
2653         
2654         Collections.sort(prefixWildcardKeys, new FaceletsVDLUtils.KeyComparator());
2655         
2656         this._prefixWildcardKeys = prefixWildcardKeys;
2657         this._contractMappings = contractMappings;
2658     }
2659 
2660     private boolean _usePartialStateSavingOnThisView(String viewId)
2661     {
2662         return _partialStateSaving && !(_viewIds != null && _viewIds.contains(viewId));
2663     }
2664 
2665     @Override
2666     public List<String> calculateResourceLibraryContracts(FacesContext context, String viewId)
2667     {
2668         List<String> contracts = this._contractMappings.get(viewId);
2669         if (contracts == null)
2670         {
2671             //Check prefix mapping
2672             for (String prefix : this._prefixWildcardKeys)
2673             {
2674                 if (FaceletsVDLUtils.matchPattern(viewId, prefix))
2675                 {
2676                     contracts =  this._contractMappings.get(prefix);
2677                     break;
2678                 }
2679             }
2680         }
2681         return contracts;
2682     }
2683 
2684     private class FaceletViewMetadata extends ViewMetadataBase
2685     {
2686         /**
2687          * Constructor
2688          *
2689          * Note that this viewId is not the one after calculateViewId() method
2690          */
2691         public FaceletViewMetadata(String viewId)
2692         {
2693             super(viewId);
2694         }
2695 
2696         @Override
2697         public UIViewRoot createMetadataView(FacesContext context)
2698         {
2699             try
2700             {
2701                 context.setProcessingEvents(false);
2702 
2703                 // spec doesn't say that this is necessary, but we blow up later if
2704                 // the viewroot isn't available from the FacesContext.
2705                 // -= Leonardo Uribe =- since it is supposed when we apply view metadata
2706                 // facelet we don't apply components with renderers and we don't call getRenderKit()
2707                 // it is safe to let this one commented
2708                 // context.setViewRoot(view);
2709 
2710                 // -= Leonardo Uribe =- This part is related to section 2.5.5 of jsf 2.0 spec.
2711                 // In theory what we need here is fill UIViewRoot.METADATA_FACET_NAME facet
2712                 // with UIViewParameter instances. Later, ViewHandlerImpl.getBookmarkableURL(),
2713                 // ViewHandlerImpl.getRedirectURL() and UIViewRoot.encodeEnd uses them. 
2714                 // For now, the only way to do this is call buildView(context,view) method, but 
2715                 // this is a waste of resources. We need to find another way to handle facelets view metadata.
2716                 // Call to buildView causes the view is not created on Render Response phase,
2717                 // if buildView is called from here all components pass through current lifecycle and only
2718                 // UIViewParameter instances should be taken into account.
2719                 // It should be an additional call to buildView on Render Response phase.
2720                 // buildView(context, view);
2721 
2722                 context.getAttributes().put(BUILDING_VIEW_METADATA, Boolean.TRUE);
2723 
2724                 // we have to invoke createView() on the application's ViewHandler
2725                 // here instead of invoking it directly in FaceletVDL, because
2726                 // the ViewHandler might be wrapped and wants to do some work
2727                 // in createView() (e.g. in Trinidad - see MYFACES-2641)
2728                 UIViewRoot view = context.getApplication().getViewHandler().createView(context, getViewId());
2729 
2730                 if (view != null)
2731                 {
2732                     // inside createView(context,viewId), calculateViewId() is called and
2733                     // the result is stored inside created UIViewRoot, so we can safely take the derived
2734                     // viewId from there.
2735                     Facelet facelet = null;
2736                     try
2737                     {
2738                         facelet = _getViewMetadataFacelet(context, view.getViewId());
2739                     }
2740                     catch (FileNotFoundException e)
2741                     {
2742                         sendSourceNotFound(context, getViewId());
2743                         return null;
2744                     }
2745 
2746                     facelet.apply(context, view);
2747                 }
2748 
2749                 return view;
2750             }
2751             catch (IOException ioe)
2752             {
2753                 throw new FacesException(ioe);
2754             }
2755             finally
2756             {
2757                 context.getAttributes().remove(BUILDING_VIEW_METADATA);
2758 
2759                 context.setProcessingEvents(true);
2760             }
2761         }
2762     }
2763     
2764     public FaceletFactory getFaceletFactory()
2765     {
2766         return _faceletFactory;
2767     }
2768 
2769     @Override
2770     public UIComponent createComponent(FacesContext context, 
2771         String taglibURI, String tagName, Map<String, Object> attributes)
2772     {
2773         checkNull(context, "context");
2774         UIComponent createdComponent = null;
2775         try
2776         {
2777             Facelet componentFacelet;
2778             FaceletFactory.setInstance(_faceletFactory);
2779             try
2780             {
2781                 componentFacelet
2782                         = _faceletFactory.compileComponentFacelet(taglibURI, tagName, attributes);
2783             }
2784             finally
2785             {
2786                 FaceletFactory.setInstance(null);
2787             }
2788             if (componentFacelet == null)
2789             {
2790                 return null;
2791             }
2792             // Create a temporal component base class where all components will be put, but we are only
2793             // interested in the inner UIComponent and if multiple are created, return this one.
2794             boolean requiresDynamicRefresh = false;
2795             boolean requiresFaceletDynamicRefresh = false;
2796             UIPanel tempParent
2797                     = (UIPanel) context.getApplication().createComponent(
2798                     context, UIPanel.COMPONENT_TYPE, null);
2799             tempParent.setId(context.getViewRoot().createUniqueId(context, null));
2800             String baseKey = tempParent.getId();
2801             baseKey = baseKey.startsWith(UIViewRoot.UNIQUE_ID_PREFIX) ? baseKey.substring(4) : baseKey;
2802 
2803             try
2804             {
2805                 tempParent.pushComponentToEL(context, tempParent);
2806                 ((AbstractFacelet)componentFacelet).applyDynamicComponentHandler(
2807                     context, tempParent, baseKey);
2808             }
2809             finally
2810             {
2811                 tempParent.popComponentFromEL(context);
2812                 // There are two cases:
2813                 // 1. If we are under facelets algorithm control (binding case), the refreshing logic will be done
2814                 // outside this block. We can check that condition easily with FaceletCompositionContext
2815                 // 2. If we are not under facelets algorithm control, check if the dynamic component requires refresh,
2816                 // if that so, mark the view to be refreshed and reset the flag, otherwise continue. This check
2817                 // allows us to decide if we add a third listener to refresh on transient build.
2818                     // Check if the current component requires dynamic refresh and if that so,
2819                 FaceletCompositionContext fcc = FaceletCompositionContext.getCurrentInstance(context);
2820                 if (fcc != null)
2821                 {
2822                     requiresFaceletDynamicRefresh = true;
2823                 }
2824                 else if (FaceletViewDeclarationLanguageBase.isDynamicComponentNeedsRefresh(context))
2825                 {
2826                     FaceletViewDeclarationLanguageBase.activateDynamicComponentRefreshTransientBuild(context);
2827                     FaceletViewDeclarationLanguageBase.resetDynamicComponentNeedsRefreshFlag(context);
2828                     requiresDynamicRefresh = true;
2829                 }
2830             }
2831             if (tempParent.getChildCount() > 1)
2832             {
2833                 // Multiple child. The tempParent will be returned. No need to
2834                 // save MARK_CREATED.
2835                 createdComponent = tempParent;
2836                 tempParent.getAttributes().put("oam.vf.DYN_WRAPPER", baseKey);
2837                 tempParent.subscribeToEvent(PostRestoreStateEvent.class, new 
2838                     RefreshDynamicComponentListener(taglibURI, tagName, attributes, baseKey));
2839                 if (requiresFaceletDynamicRefresh)
2840                 {
2841                     FaceletViewDeclarationLanguageBase.dynamicComponentNeedsRefresh(context);
2842                 }
2843             }
2844             else if (tempParent.getChildCount() == 1)
2845             {
2846                 createdComponent = tempParent.getChildren().get(0);
2847                 boolean requiresRefresh = false;
2848                 // One child. In that case there are three choices:
2849                 if (UIComponent.isCompositeComponent(createdComponent))
2850                 {
2851                     // 1. Composite component. Needs special handling because
2852                     // facets will be added programatically. The algorithm that
2853                     // process the composite component content should occur
2854                     // after the component is added to the view (PostAddToViewEvent).
2855                     // Requires refresh. To do that, we need to save the MARK_CREATED
2856                     // value and set it only when the full component is refreshed after 
2857                     // restore it.
2858                     createdComponent.getAttributes().put("oam.vf.GEN_MARK_ID",
2859                         createdComponent.getAttributes().get(ComponentSupport.MARK_CREATED));
2860                     createdComponent.getAttributes().put(ComponentSupport.MARK_CREATED, null);
2861                     createdComponent.subscribeToEvent(PostAddToViewEvent.class, new 
2862                         CreateDynamicCompositeComponentListener(taglibURI, tagName, attributes, baseKey));
2863                     requiresRefresh = true;
2864                     if (requiresFaceletDynamicRefresh)
2865                     {
2866                         FaceletViewDeclarationLanguageBase.dynamicComponentNeedsRefresh(context);
2867                     }
2868                 }
2869                 else if (createdComponent.getChildCount() > 0)
2870                 {
2871                     // 2. Single component with children inside.
2872                     // Requires refresh. To do that, we need to save the MARK_CREATED
2873                     // value and set it only when the full component is refreshed after 
2874                     // restore it.
2875                     createdComponent.getAttributes().put("oam.vf.GEN_MARK_ID",
2876                         createdComponent.getAttributes().get(ComponentSupport.MARK_CREATED));
2877                     createdComponent.getAttributes().put(ComponentSupport.MARK_CREATED, null);
2878                     requiresRefresh = true;
2879                     if (requiresFaceletDynamicRefresh)
2880                     {
2881                         FaceletViewDeclarationLanguageBase.dynamicComponentNeedsRefresh(context);
2882                     }
2883                 }
2884                 else if (createdComponent.isTransient())
2885                 {
2886                     // Just transient markup inside. It is necessary to wrap
2887                     // that content into a component. Requires refresh. No need to
2888                     // save MARK_CREATED. No requires dynamic refresh.
2889                     createdComponent = tempParent;
2890                     tempParent.getAttributes().put("oam.vf.DYN_WRAPPER", baseKey);
2891                     requiresRefresh = true;
2892                 }
2893                 else
2894                 {
2895                     // 4. Single component without children: 
2896                     // Remove MARK_CREATED because it does not requires
2897                     // refresh on restore. When it is added to the component
2898                     // tree, it will be saved and restored as if was a programatically
2899                     // added component.
2900                     createdComponent.getAttributes().put(ComponentSupport.MARK_CREATED, null);
2901                 }
2902                 if (requiresRefresh)
2903                 {
2904                     createdComponent.subscribeToEvent(PostRestoreStateEvent.class, new 
2905                         RefreshDynamicComponentListener(taglibURI, tagName, attributes, baseKey));
2906                 }
2907                 if (requiresDynamicRefresh)
2908                 {
2909                     createdComponent.subscribeToEvent(DynamicComponentRefreshTransientBuildEvent.class, new 
2910                         RefreshDynamicComponentListener(taglibURI, tagName, attributes, baseKey));
2911                     createdComponent.getAttributes().put(
2912                             DynamicComponentRefreshTransientBuildEvent.DYN_COMP_REFRESH_FLAG, Boolean.TRUE);
2913                 }
2914                 if (requiresFaceletDynamicRefresh)
2915                 {
2916                     createdComponent.subscribeToEvent(FaceletDynamicComponentRefreshTransientBuildEvent.class, new 
2917                         RefreshDynamicComponentListener(taglibURI, tagName, attributes, baseKey));
2918                 }
2919             }
2920         }
2921         catch (IOException e)
2922         {
2923             throw new FacesException(e);
2924         }
2925         return createdComponent;
2926     }
2927     
2928     protected RenderKitFactory getRenderKitFactory()
2929     {
2930         if (_renderKitFactory == null)
2931         {
2932             _renderKitFactory = (RenderKitFactory)FactoryFinder.getFactory(FactoryFinder.RENDER_KIT_FACTORY);
2933         }
2934         return _renderKitFactory;
2935     }
2936 }