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.lang.reflect.Array;
28  import java.net.URL;
29  import java.util.ArrayList;
30  import java.util.Collections;
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.application.ProjectStage;
46  import javax.faces.application.Resource;
47  import javax.faces.application.StateManager;
48  import javax.faces.application.ViewHandler;
49  import javax.faces.component.ActionSource2;
50  import javax.faces.component.EditableValueHolder;
51  import javax.faces.component.UIComponent;
52  import javax.faces.component.UINamingContainer;
53  import javax.faces.component.UIViewRoot;
54  import javax.faces.context.ExternalContext;
55  import javax.faces.context.FacesContext;
56  import javax.faces.context.ResponseWriter;
57  import javax.faces.event.ActionEvent;
58  import javax.faces.event.ActionListener;
59  import javax.faces.event.MethodExpressionActionListener;
60  import javax.faces.event.MethodExpressionValueChangeListener;
61  import javax.faces.event.PhaseId;
62  import javax.faces.event.PostAddToViewEvent;
63  import javax.faces.event.PreRemoveFromViewEvent;
64  import javax.faces.event.ValueChangeEvent;
65  import javax.faces.event.ValueChangeListener;
66  import javax.faces.render.RenderKit;
67  import javax.faces.validator.MethodExpressionValidator;
68  import javax.faces.validator.Validator;
69  import javax.faces.view.ActionSource2AttachedObjectHandler;
70  import javax.faces.view.ActionSource2AttachedObjectTarget;
71  import javax.faces.view.AttachedObjectHandler;
72  import javax.faces.view.AttachedObjectTarget;
73  import javax.faces.view.BehaviorHolderAttachedObjectHandler;
74  import javax.faces.view.BehaviorHolderAttachedObjectTarget;
75  import javax.faces.view.EditableValueHolderAttachedObjectHandler;
76  import javax.faces.view.EditableValueHolderAttachedObjectTarget;
77  import javax.faces.view.StateManagementStrategy;
78  import javax.faces.view.ValueHolderAttachedObjectHandler;
79  import javax.faces.view.ValueHolderAttachedObjectTarget;
80  import javax.faces.view.ViewMetadata;
81  import javax.faces.view.facelets.FaceletContext;
82  import javax.faces.view.facelets.ResourceResolver;
83  import javax.faces.view.facelets.TagDecorator;
84  import javax.servlet.http.HttpServletResponse;
85  
86  import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFWebConfigParam;
87  import org.apache.myfaces.config.RuntimeConfig;
88  import org.apache.myfaces.shared.application.DefaultViewHandlerSupport;
89  import org.apache.myfaces.shared.application.ViewHandlerSupport;
90  import org.apache.myfaces.shared.config.MyfacesConfig;
91  import org.apache.myfaces.shared.util.ClassUtils;
92  import org.apache.myfaces.shared.util.StringUtils;
93  import org.apache.myfaces.shared.util.WebConfigParamUtils;
94  import org.apache.myfaces.shared.view.ViewDeclarationLanguageBase;
95  import org.apache.myfaces.view.ViewMetadataBase;
96  import org.apache.myfaces.view.facelets.FaceletViewHandler.NullWriter;
97  import org.apache.myfaces.view.facelets.compiler.Compiler;
98  import org.apache.myfaces.view.facelets.compiler.SAXCompiler;
99  import org.apache.myfaces.view.facelets.compiler.TagLibraryConfig;
100 import org.apache.myfaces.view.facelets.el.CompositeComponentELUtils;
101 import org.apache.myfaces.view.facelets.el.LocationMethodExpression;
102 import org.apache.myfaces.view.facelets.el.LocationValueExpression;
103 import org.apache.myfaces.view.facelets.el.MethodExpressionMethodExpression;
104 import org.apache.myfaces.view.facelets.el.RedirectMethodExpressionValueExpressionActionListener;
105 import org.apache.myfaces.view.facelets.el.RedirectMethodExpressionValueExpressionValidator;
106 import org.apache.myfaces.view.facelets.el.RedirectMethodExpressionValueExpressionValueChangeListener;
107 import org.apache.myfaces.view.facelets.el.ValueExpressionMethodExpression;
108 import org.apache.myfaces.view.facelets.el.VariableMapperWrapper;
109 import org.apache.myfaces.view.facelets.impl.DefaultFaceletFactory;
110 import org.apache.myfaces.view.facelets.impl.DefaultResourceResolver;
111 import org.apache.myfaces.view.facelets.tag.TagLibrary;
112 import org.apache.myfaces.view.facelets.tag.composite.ClientBehaviorAttachedObjectTarget;
113 import org.apache.myfaces.view.facelets.tag.composite.ClientBehaviorRedirectBehaviorAttachedObjectHandlerWrapper;
114 import org.apache.myfaces.view.facelets.tag.composite.ClientBehaviorRedirectEventComponentWrapper;
115 import org.apache.myfaces.view.facelets.tag.composite.CompositeLibrary;
116 import org.apache.myfaces.view.facelets.tag.composite.CompositeResourceLibrary;
117 import org.apache.myfaces.view.facelets.tag.jsf.ComponentSupport;
118 import org.apache.myfaces.view.facelets.tag.jsf.core.AjaxHandler;
119 import org.apache.myfaces.view.facelets.tag.jsf.core.CoreLibrary;
120 import org.apache.myfaces.view.facelets.tag.jsf.html.HtmlLibrary;
121 import org.apache.myfaces.view.facelets.tag.jstl.core.JstlCoreLibrary;
122 import org.apache.myfaces.view.facelets.tag.jstl.fn.JstlFnLibrary;
123 import org.apache.myfaces.view.facelets.tag.ui.UIDebug;
124 import org.apache.myfaces.view.facelets.tag.ui.UILibrary;
125 import org.apache.myfaces.view.facelets.util.ReflectionUtil;
126 
127 import org.apache.myfaces.view.facelets.impl.SectionUniqueIdCounter;
128 import org.apache.myfaces.view.facelets.tag.jsf.PartialMethodExpressionActionListener;
129 import org.apache.myfaces.view.facelets.tag.jsf.PartialMethodExpressionValidator;
130 import org.apache.myfaces.view.facelets.tag.jsf.PartialMethodExpressionValueChangeListener;
131 
132 /**
133  * This class represents the abstraction of Facelets as a ViewDeclarationLanguage.
134  *
135  * @author Simon Lessard (latest modification by $Author: lu4242 $)
136  * @version $Revision: 1523352 $ $Date: 2013-09-14 18:28:22 -0500 (Sat, 14 Sep 2013) $
137  *
138  * @since 2.0
139  */
140 public class FaceletViewDeclarationLanguage extends ViewDeclarationLanguageBase
141 {
142     //private static final Log log = LogFactory.getLog(FaceletViewDeclarationLanguage.class);
143     private static final Logger log = Logger.getLogger(FaceletViewDeclarationLanguage.class.getName());
144 
145     private static final Class<?>[] EMPTY_CLASS_ARRAY = new Class<?>[0];
146 
147     private static final Class<?>[] VALUE_CHANGE_LISTENER_SIGNATURE = new Class[]{ValueChangeEvent.class};
148 
149     private static final Class<?>[] ACTION_LISTENER_SIGNATURE = new Class[]{ActionEvent.class};
150 
151     private static final Class<?>[] VALIDATOR_SIGNATURE
152             = new Class[]{FacesContext.class, UIComponent.class, Object.class};
153 
154     public static final String CHARACTER_ENCODING_KEY = "javax.faces.request.charset";
155 
156     public final static long DEFAULT_REFRESH_PERIOD = 2;
157     public final static long DEFAULT_REFRESH_PERIOD_PRODUCTION = -1;
158 
159     public final static String DEFAULT_CHARACTER_ENCODING = "UTF-8";
160 
161     //public final static String PARAM_BUFFER_SIZE = "javax.faces.FACELETS_BUFFER_SIZE";
162 
163     /**
164      * Define the default buffer size value passed to ExternalContext.setResponseBufferResponse() and in a
165      * servlet environment to HttpServletResponse.setBufferSize().
166      */
167     @JSFWebConfigParam(since = "2.0", alias = "facelets.BUFFER_SIZE", classType = "java.lang.Integer",
168             tags = "performance",
169             desc = "Define the default buffer size value passed to ExternalContext.setResponseBufferResponse() and in "
170                    + "a servlet environment to HttpServletResponse.setBufferSize()")
171     public final static String PARAM_BUFFER_SIZE = "javax.faces.FACELETS_BUFFER_SIZE";
172 
173     /**
174      * Define the default buffer size value passed to ExternalContext.setResponseBufferResponse() and in a
175      * servlet environment to HttpServletResponse.setBufferSize().
176      */
177     @JSFWebConfigParam(since = "2.0", deprecated = true, classType = "java.lang.Integer")
178     private final static String PARAM_BUFFER_SIZE_DEPRECATED = "facelets.BUFFER_SIZE";
179 
180     private final static String[] PARAMS_BUFFER_SIZE = {PARAM_BUFFER_SIZE, PARAM_BUFFER_SIZE_DEPRECATED};
181 
182     //private final static String PARAM_BUILD_BEFORE_RESTORE = "facelets.BUILD_BEFORE_RESTORE";
183 
184     /**
185      * Set of class names, separated by ';', implementing TagDecorator interface, used to transform
186      * a view definition in a facelet abstract syntax tree, that is used later to generate a component tree.
187      */
188     @JSFWebConfigParam(since = "2.0", alias = "facelets.DECORATORS")
189     public final static String PARAM_DECORATORS = "javax.faces.FACELETS_DECORATORS";
190 
191     /**
192      * Set of class names, separated by ';', implementing TagDecorator interface, used to transform
193      * a view definition in a facelet abstract syntax tree, that is used later to generate a component tree.
194      */
195     @JSFWebConfigParam(since = "2.0", deprecated = true)
196     private final static String PARAM_DECORATORS_DEPRECATED = "facelets.DECORATORS";
197 
198     private final static String[] PARAMS_DECORATORS = {PARAM_DECORATORS, PARAM_DECORATORS_DEPRECATED};
199 
200     /**
201      * Constant used by EncodingHandler to indicate the current encoding of the page being built,
202      * and indicate which one is the response encoding on getResponseEncoding(FacesContext, String) method.
203      */
204     public final static String PARAM_ENCODING = "facelets.Encoding";
205 
206     /**
207      * Set of .taglib.xml files, separated by ';' that should be loaded by facelet engine.
208      */
209     @JSFWebConfigParam(since = "2.0",
210             desc = "Set of .taglib.xml files, separated by ';' that should be loaded by facelet engine.",
211             alias = "facelets.LIBRARIES")
212     public final static String PARAM_LIBRARIES = "javax.faces.FACELETS_LIBRARIES";
213 
214     /**
215      * Set of .taglib.xml files, separated by ';' that should be loaded by facelet engine.
216      */
217     @JSFWebConfigParam(since = "2.0",
218             desc = "Set of .taglib.xml files, separated by ';' that should be loaded by facelet engine.",
219             deprecated = true)
220     private final static String PARAM_LIBRARIES_DEPRECATED = "facelets.LIBRARIES";
221 
222     private final static String[] PARAMS_LIBRARIES = {PARAM_LIBRARIES, PARAM_LIBRARIES_DEPRECATED};
223 
224     /**
225      * Define the period used to refresh the facelet abstract syntax tree from the view definition file. 
226      *
227      * <p>By default is infinite (no active).</p>
228      */
229     @JSFWebConfigParam(since = "2.0", defaultValue = "-1", alias = "facelets.REFRESH_PERIOD",
230             classType = "java.lang.Long", tags = "performance")
231     public final static String PARAM_REFRESH_PERIOD = "javax.faces.FACELETS_REFRESH_PERIOD";
232 
233     /**
234      * Define the period used to refresh the facelet abstract syntax tree from the view definition file. 
235      *
236      * <p>By default is infinite (no active).</p>
237      */
238     @JSFWebConfigParam(since = "2.0", defaultValue = "-1", deprecated = true)
239     private final static String PARAM_REFRESH_PERIOD_DEPRECATED = "facelets.REFRESH_PERIOD";
240 
241     private final static String[] PARAMS_REFRESH_PERIOD = {PARAM_REFRESH_PERIOD, PARAM_REFRESH_PERIOD_DEPRECATED};
242 
243     /**
244      * Class implementing ResourceResolver interface used to locate facelet resources. 
245      */
246     @JSFWebConfigParam(since = "2.0", alias = "facelets.RESOURCE_RESOLVER")
247     public final static String PARAM_RESOURCE_RESOLVER = "javax.faces.FACELETS_RESOURCE_RESOLVER";
248 
249     /**
250      * Class implementing ResourceResolver interface used to locate facelet resources.
251      */
252     @JSFWebConfigParam(since = "2.0", deprecated = true)
253     private final static String PARAM_RESOURCE_RESOLVER_DEPRECATED = "facelets.RESOURCE_RESOLVER";
254 
255     private final static String[] PARAMS_RESOURCE_RESOLVER
256             = {PARAM_RESOURCE_RESOLVER, PARAM_RESOURCE_RESOLVER_DEPRECATED};
257 
258     /**
259      * Skip comments found on a facelet file.
260      */
261     @JSFWebConfigParam(since = "2.0", alias = "facelets.SKIP_COMMENTS")
262     public final static String PARAM_SKIP_COMMENTS = "javax.faces.FACELETS_SKIP_COMMENTS";
263 
264     /**
265      * Skip comments found on a facelet file.
266      */
267     @JSFWebConfigParam(since = "2.0", deprecated = true)
268     private final static String PARAM_SKIP_COMMENTS_DEPRECATED = "facelets.SKIP_COMMENTS";
269 
270     private final static String[] PARAMS_SKIP_COMMENTS = {PARAM_SKIP_COMMENTS, PARAM_SKIP_COMMENTS_DEPRECATED};
271 
272     //public final static String PARAM_VIEW_MAPPINGS = "javax.faces.FACELETS_VIEW_MAPPINGS";
273 
274     //private final static String PARAM_VIEW_MAPPINGS_DEPRECATED = "facelets.VIEW_MAPPINGS";
275 
276     public final static String FILLED_VIEW = "org.apache.myfaces.FILLED_VIEW";
277 
278     //BEGIN CONSTANTS SET ON BUILD VIEW
279     //public final static String BUILDING_COMPOSITE_COMPONENT_METADATA = "org.apache.myfaces.BUILDING_COMPOSITE_COMPONENT_METADATA";
280 
281     public final static String BUILDING_VIEW_METADATA = "org.apache.myfaces.BUILDING_VIEW_METADATA";
282 
283     public final static String REFRESHING_TRANSIENT_BUILD = "org.apache.myfaces.REFRESHING_TRANSIENT_BUILD";
284 
285     public final static String REFRESH_TRANSIENT_BUILD_ON_PSS = "org.apache.myfaces.REFRESH_TRANSIENT_BUILD_ON_PSS";
286 
287     public final static String USING_PSS_ON_THIS_VIEW = "org.apache.myfaces.USING_PSS_ON_THIS_VIEW";
288 
289     public final static String REMOVING_COMPONENTS_BUILD = "org.apache.myfaces.REMOVING_COMPONENTS_BUILD";
290     //END CONSTANTS SET ON BUILD VIEW
291 
292     /**
293      * Marker to indicate tag handlers the view currently being built is using
294      * partial state saving and it is necessary to call UIComponent.markInitialState
295      * after component instances are populated. 
296      */
297     public final static String MARK_INITIAL_STATE_KEY = "org.apache.myfaces.MARK_INITIAL_STATE";
298     
299     public final static String IS_BUILDING_INITIAL_STATE_KEY_ALIAS
300             = "javax.faces.view.ViewDeclarationLanguage.IS_BUILDING_INITIAL_STATE";
301 
302     public final static String CLEAN_TRANSIENT_BUILD_ON_RESTORE
303             = "org.apache.myfaces.CLEAN_TRANSIENT_BUILD_ON_RESTORE";
304 
305     private final static String STATE_KEY = "<!--@@JSF_FORM_STATE_MARKER@@-->";
306 
307     private static final String IS_BUILDING_INITIAL_STATE = "javax.faces.IS_BUILDING_INITIAL_STATE";
308 
309     private final static int STATE_KEY_LEN = STATE_KEY.length();
310     
311     /**
312      * Key used to cache component ids for the counter
313      */
314     public final static String CACHED_COMPONENT_IDS = "oam.CACHED_COMPONENT_IDS"; 
315 
316     private int _bufferSize;
317 
318     // This param evolve in jsf 2.0 to partial state saving
319     //private boolean _buildBeforeRestore = false;
320 
321     private ViewHandlerSupport _cachedViewHandlerSupport;
322 
323     private String _defaultSuffix;
324 
325     private FaceletFactory _faceletFactory;
326 
327     private StateManagementStrategy _stateMgmtStrategy;
328 
329     private boolean _partialStateSaving;
330 
331     private boolean _refreshTransientBuildOnPSS;
332 
333     private boolean _refreshTransientBuildOnPSSAuto;
334 
335     private Set<String> _viewIds;
336 
337     /**
338      *
339      */
340     public FaceletViewDeclarationLanguage(FacesContext context)
341     {
342         initialize(context);
343     }
344 
345     /**
346      * {@inheritDoc}
347      */
348     @Override
349     public void buildView(FacesContext context, UIViewRoot view) throws IOException
350     {
351         if (isFilledView(context, view))
352         {
353             return;
354         }
355 
356         // setup our viewId
357         String previousViewId = view.getViewId();
358         String renderedViewId = getRenderedViewId(context, previousViewId);
359 
360         if (renderedViewId == null)
361         {
362             view.setViewId(renderedViewId);
363         }
364         else if (!renderedViewId.equals(previousViewId))
365         {
366             view.setViewId(renderedViewId);
367         }
368 
369         if (log.isLoggable(Level.FINEST))
370         {
371             log.finest("Building View: " + renderedViewId);
372         }
373 
374         boolean usePartialStateSavingOnThisView = _usePartialStateSavingOnThisView(renderedViewId);
375         boolean refreshTransientBuild = (view.getChildCount() > 0);
376         boolean refreshTransientBuildOnPSS = (usePartialStateSavingOnThisView && _refreshTransientBuildOnPSS);
377 
378         if (usePartialStateSavingOnThisView)
379         {
380             // Before apply we need to make sure the current view has
381             // a clientId that will be used as a key to save and restore
382             // the current view. Note that getClientId is never called (or used)
383             // from UIViewRoot.
384             if (view.getId() == null)
385             {
386                 view.setId(view.createUniqueId(context, null));
387             }
388 
389             context.getAttributes().put(USING_PSS_ON_THIS_VIEW, Boolean.TRUE);
390             //Add a key to indicate ComponentTagHandlerDelegate to 
391             //call UIComponent.markInitialState after it is populated
392             if (!refreshTransientBuild)
393             {
394                 context.getAttributes().put(MARK_INITIAL_STATE_KEY, Boolean.TRUE);
395                 context.getAttributes().put(IS_BUILDING_INITIAL_STATE, Boolean.TRUE);
396                 context.getAttributes().put(IS_BUILDING_INITIAL_STATE_KEY_ALIAS, Boolean.TRUE);
397             }
398             if (refreshTransientBuildOnPSS)
399             {
400                 //This value is only set when _refreshTransientBuildOnPSSMode is "auto" or "true" 
401                 context.getAttributes().put(REFRESH_TRANSIENT_BUILD_ON_PSS, _refreshTransientBuildOnPSSAuto ? "auto" : "true");
402             }
403         }
404 
405         try
406         {
407             if (refreshTransientBuild)
408             {
409                 context.getAttributes().put(REFRESHING_TRANSIENT_BUILD, Boolean.TRUE);
410 
411                 // In theory, this should be disabled on ComponentTagHandlerDelegate,
412                 // otherwise we could lost PostAddToViewEvent / PreRemoveFromViewEvent
413                 // caused by c:if effect or facelets cleanup algorithm
414                 //context.setProcessingEvents(false);
415             }
416             // populate UIViewRoot
417             _getFacelet(renderedViewId).apply(context, view);
418         }
419         finally
420         {
421             if (refreshTransientBuildOnPSS)
422             {
423                 context.getAttributes().remove(REFRESH_TRANSIENT_BUILD_ON_PSS);
424             }
425             if (refreshTransientBuild)
426             {
427                 //context.setProcessingEvents(true);
428 
429                 if (!usePartialStateSavingOnThisView || refreshTransientBuildOnPSS)
430                 {
431                     // When the facelet is applied, all components are removed and added from view,
432                     // but the difference resides in the ordering. Since the view is
433                     // being refreshed, if we don't do this manually, some tags like
434                     // cc:insertChildren or cc:insertFacet will not work correctly, because
435                     // we expect PostAddToViewEvent will be propagated from parent to child, and
436                     // facelets refreshing algorithm do the opposite.
437                     //FaceletViewDeclarationLanguage._publishPreRemoveFromViewEvent(context, view);
438                     //FaceletViewDeclarationLanguage._publishPostAddToViewEvent(context, view);
439                     FaceletViewDeclarationLanguage._publishPostBuildComponentTreeOnRestoreViewEvent(context, view);
440                 }
441 
442                 context.getAttributes().remove(REFRESHING_TRANSIENT_BUILD);
443             }
444             else
445             {
446                 // Publish PostAddToView over UIViewRoot, because this is not done automatically.
447                 context.getApplication().publishEvent(context, PostAddToViewEvent.class, UIViewRoot.class, view);
448             }
449         }
450 
451         // set this view as filled
452         if (refreshTransientBuild)
453         {
454             //This option will be true on this cases:
455             //- pss is false, but we are refreshing
456             //- pss is true, and we are refreshing a view already filled
457             setFilledView(context, view);
458         }
459         else if (!refreshTransientBuildOnPSS)
460         {
461             // This option will be true on this cases:
462             // -pss is true and refresh is not active
463             setFilledView(context, view);
464         }
465         //At this point refreshTransientBuild = false && refreshTransientBuildOnPSS is true
466         else if (_refreshTransientBuildOnPSSAuto &&
467                  !context.getAttributes().containsKey(CLEAN_TRANSIENT_BUILD_ON_RESTORE))
468         {
469             setFilledView(context, view);
470         }
471 
472         // Suscribe listeners if we are using partialStateSaving
473         if (usePartialStateSavingOnThisView)
474         {
475             // UIViewRoot.markInitialState() is not called because it does
476             // not have a facelet tag handler class that create it, instead
477             // new instances are created programatically.
478             if (!refreshTransientBuild)
479             {
480                 if (!refreshTransientBuildOnPSS ||
481                     !view.getAttributes().containsKey(DefaultFaceletsStateManagementStrategy.COMPONENT_ADDED_AFTER_BUILD_VIEW))
482                 {
483                     view.markInitialState();
484                 }
485 
486                 //Remove the key that indicate we need to call UIComponent.markInitialState
487                 //on the current tree
488                 context.getAttributes().remove(MARK_INITIAL_STATE_KEY);
489                 context.getAttributes().remove(IS_BUILDING_INITIAL_STATE);
490                 context.getAttributes().remove(IS_BUILDING_INITIAL_STATE_KEY_ALIAS);
491             }
492 
493             // We need to suscribe the listeners of changes in the component tree
494             // only the first time here. Later we suscribe this listeners on
495             // DefaultFaceletsStateManagement.restoreView after calling 
496             // _publishPostBuildComponentTreeOnRestoreViewEvent(), to ensure 
497             // relocated components are not retrieved later on getClientIdsRemoved().
498             if (!(refreshTransientBuild && PhaseId.RESTORE_VIEW.equals(context.getCurrentPhaseId())))
499             {
500                 ((DefaultFaceletsStateManagementStrategy) getStateManagementStrategy(context, view.getViewId())).suscribeListeners(view);
501             }
502 
503             context.getAttributes().remove(USING_PSS_ON_THIS_VIEW);
504         }
505 
506         // Remove this var from faces context because this one prevent AjaxHandler
507         // register the standard script library on Post-Redirect-Get pattern or
508         // in the next view
509         context.getAttributes().remove(AjaxHandler.STANDARD_JSF_AJAX_LIBRARY_LOADED);
510     }
511 
512     private static void _publishPreRemoveFromViewEvent(FacesContext context, UIComponent component)
513     {
514         context.getApplication().publishEvent(context, PreRemoveFromViewEvent.class, component.getClass(), component);
515 
516         if (component.getChildCount() > 0)
517         {
518             for (int j = 0, childCount = component.getChildCount(); j < childCount; j++)
519             {
520                 UIComponent child = component.getChildren().get(j);
521                 _publishPreRemoveFromViewEvent(context, child);
522             }
523         }
524         if (component.getFacetCount() > 0)
525         {
526             for (UIComponent child : component.getFacets().values())
527             {
528                 _publishPreRemoveFromViewEvent(context, child);
529             }
530         }
531     }
532 
533     public static void _publishPostBuildComponentTreeOnRestoreViewEvent(FacesContext context, UIComponent component)
534     {
535         context.getApplication().publishEvent(context, PostBuildComponentTreeOnRestoreViewEvent.class,
536                                               component.getClass(), component);
537 
538         if (component.getChildCount() > 0)
539         {
540             // PostAddToViewEvent could cause component relocation
541             // (h:outputScript, h:outputStylesheet, composite:insertChildren, composite:insertFacet)
542             // so we need to check if the component was relocated or not
543             List<UIComponent> children = component.getChildren();
544             UIComponent child = null;
545             UIComponent currentChild = null;
546             int i = 0;
547             while (i < children.size())
548             {
549                 child = children.get(i);
550                 // Iterate over the same index if the component was removed
551                 // This prevents skip components when processing
552                 do
553                 {
554                     _publishPostBuildComponentTreeOnRestoreViewEvent(context, child);
555                     currentChild = child;
556                     child = children.get(i);
557                 }
558                 while ((i < children.size()) && child != currentChild);
559                 i++;
560             }
561         }
562         if (component.getFacetCount() > 0)
563         {
564             for (UIComponent child : component.getFacets().values())
565             {
566                 _publishPostBuildComponentTreeOnRestoreViewEvent(context, child);
567             }
568         }
569     }
570 
571     private boolean isFilledView(FacesContext context, UIViewRoot view)
572     {
573         // The view is only built on restoreView or renderView, but if
574         // we are not using partial state saving, we need to mark the current
575         // view as filled, otherwise it will be filled again on renderView.
576         return context.getAttributes().containsKey(view);
577         // -= Leonardo Uribe =- save this key on view cause render fail, because the view
578         // is built before render view to "restore" the transient components that has
579         // facelet markup (facelets UIInstructions ...) This effect is only notice when
580         // partial state saving is not used. 
581         //return view.getAttributes().containsKey(FILLED_VIEW);
582     }
583 
584     private void setFilledView(FacesContext context, UIViewRoot view)
585     {
586         context.getAttributes().put(view, Boolean.TRUE);
587         // -= Leonardo Uribe =- save this key on view cause render fail, because the view
588         // is built before render view to "restore" the transient components that has
589         // facelet markup (facelets UIInstructions ...) This effect is only notice when
590         // partial state saving is not used. 
591         // view.getAttributes().put(FILLED_VIEW, Boolean.TRUE);
592     }
593 
594     /**
595      * retargetMethodExpressions(FacesContext, UIComponent) has some clues about the behavior of this method
596      *
597      * {@inheritDoc}
598      */
599     @Override
600     public BeanInfo getComponentMetadata(FacesContext context, Resource componentResource)
601     {
602         BeanInfo beanInfo = null;
603 
604         try
605         {
606             Facelet compositeComponentFacelet;
607             FaceletFactory.setInstance(_faceletFactory);
608             try
609             {
610                 compositeComponentFacelet
611                         = _faceletFactory.getCompositeComponentMetadataFacelet(componentResource.getURL());
612             }
613             finally
614             {
615                 FaceletFactory.setInstance(null);
616             }
617             //context.getAttributes().put(BUILDING_COMPOSITE_COMPONENT_METADATA, Boolean.TRUE);
618 
619             // Create a temporal tree where all components will be put, but we are only
620             // interested in metadata.
621             UINamingContainer compositeComponentBase
622                     = (UINamingContainer) context.getApplication().createComponent(
623                     context, UINamingContainer.COMPONENT_TYPE, null);
624 
625             // Fill the component resource key, because this information should be available
626             // on metadata to recognize which is the component used as composite component base.
627             // Since this method is called from Application.createComponent(FacesContext,Resource),
628             // and in that specific method this key is updated, this is the best option we
629             // have for recognize it (also this key is used by UIComponent.isCompositeComponent)
630             compositeComponentBase.getAttributes().put(Resource.COMPONENT_RESOURCE_KEY, componentResource);
631 
632             // According to UserTagHandler, in this point we need to wrap the facelet
633             // VariableMapper, so local changes are applied on "page context", but
634             // data is retrieved from full context
635             FaceletContext faceletContext = (FaceletContext) context.
636                     getAttributes().get(FaceletContext.FACELET_CONTEXT_KEY);
637             VariableMapper orig = faceletContext.getVariableMapper();
638             try
639             {
640                 faceletContext.setVariableMapper(new VariableMapperWrapper(orig));
641 
642                 compositeComponentBase.pushComponentToEL(context, compositeComponentBase);
643 
644                 compositeComponentFacelet.apply(context, compositeComponentBase);
645 
646                 compositeComponentBase.popComponentFromEL(context);
647             }
648             finally
649             {
650                 faceletContext.setVariableMapper(orig);
651             }
652 
653             beanInfo = (BeanInfo) compositeComponentBase.getAttributes().get(UIComponent.BEANINFO_KEY);
654         }
655         catch (IOException e)
656         {
657             throw new FacesException(e);
658         }
659         //finally
660         //{
661         //context.getAttributes().remove(BUILDING_COMPOSITE_COMPONENT_METADATA);
662         //}
663 
664         return beanInfo;
665     }
666 
667     /**
668      * Check if the current facelet applied is used to build composite component metadata.
669      *
670      * @param context
671      * @return
672      */
673     //public static boolean isBuildingCompositeComponentMetadata(FacesContext context)
674     //{
675     //    return context.getAttributes().containsKey(BUILDING_COMPOSITE_COMPONENT_METADATA);
676     //}
677 
678     /**
679      * Check if the current facelet applied is used to build view metadata.
680      *
681      * @param context
682      * @return
683      */
684     public static boolean isBuildingViewMetadata(FacesContext context)
685     {
686         return context.getAttributes().containsKey(BUILDING_VIEW_METADATA);
687     }
688 
689     public static boolean isRefreshingTransientBuild(FacesContext context)
690     {
691         return context.getAttributes().containsKey(REFRESHING_TRANSIENT_BUILD);
692     }
693 
694     public static boolean isRemovingComponentBuild(FacesContext context)
695     {
696         return context.getAttributes().containsKey(REMOVING_COMPONENTS_BUILD);
697     }
698 
699     public static boolean isMarkInitialState(FacesContext context)
700     {
701         return Boolean.TRUE.equals(context.getAttributes().get(MARK_INITIAL_STATE_KEY));
702     }
703 
704     public static boolean isRefreshTransientBuildOnPSS(FacesContext context)
705     {
706         //this include both "true" and "auto"
707         return context.getAttributes().containsKey(REFRESH_TRANSIENT_BUILD_ON_PSS);
708     }
709 
710     public static boolean isRefreshTransientBuildOnPSSAuto(FacesContext context)
711     {
712         return "auto".equalsIgnoreCase((String) context.getAttributes().get(REFRESH_TRANSIENT_BUILD_ON_PSS));
713     }
714 
715     public static boolean isCleanTransientBuildOnRestore(FacesContext context)
716     {
717         return context.getAttributes().containsKey(CLEAN_TRANSIENT_BUILD_ON_RESTORE);
718     }
719 
720     public static void cleanTransientBuildOnRestore(FacesContext context)
721     {
722         context.getAttributes().put(CLEAN_TRANSIENT_BUILD_ON_RESTORE, Boolean.TRUE);
723     }
724 
725     public static boolean isUsingPSSOnThisView(FacesContext context)
726     {
727         return context.getAttributes().containsKey(USING_PSS_ON_THIS_VIEW);
728     }
729 
730     /**
731      * In short words, this method take care of "target" an "attached object".
732      * <ul>
733      * <li>The "attached object" is instantiated by a tag handler.</li> 
734      * <li>The "target" is an object used as "marker", that exposes a List<UIComponent></li>
735      * </ul>
736      * This method should be called from some composite component tag handler, after
737      * all children of composite component has been applied.
738      */
739     @Override
740     @SuppressWarnings("unchecked")
741     public void retargetAttachedObjects(FacesContext context,
742                                         UIComponent topLevelComponent, List<AttachedObjectHandler> handlerList)
743     {
744         BeanInfo compositeComponentMetadata
745                 = (BeanInfo) topLevelComponent.getAttributes().get(UIComponent.BEANINFO_KEY);
746 
747         if (compositeComponentMetadata == null)
748         {
749             log.severe("Composite component metadata not found for: " + topLevelComponent.getClientId(context));
750             return;
751         }
752 
753         BeanDescriptor compositeComponentDescriptor = compositeComponentMetadata.getBeanDescriptor();
754 
755         List<AttachedObjectTarget> targetList = (List<AttachedObjectTarget>)
756                 compositeComponentDescriptor.getValue(AttachedObjectTarget.ATTACHED_OBJECT_TARGETS_KEY);
757 
758         if (targetList == null || targetList.isEmpty())
759         {
760             return;
761         }
762 
763         for (int i = 0, size = handlerList.size(); i < size; i++)
764         {
765             AttachedObjectHandler currentHandler = handlerList.get(i);
766             // In the spec javadoc this variable is referred as forAttributeValue, but
767             // note it is also called curTargetName
768             String forValue = currentHandler.getFor();
769             
770             // perf: targetList is always arrayList: see AttachedObjectTargetHandler.apply 
771             // and ClientBehaviorHandler.apply 
772             for (int k = 0, targetsSize = targetList.size(); k < targetsSize; k++)
773             {
774                 AttachedObjectTarget currentTarget = targetList.get(k);
775                 FaceletCompositionContext mctx = FaceletCompositionContext.getCurrentInstance();
776 
777                 if ((forValue != null && forValue.equals(currentTarget.getName())) &&
778                         ((currentTarget instanceof ActionSource2AttachedObjectTarget &&
779                                 currentHandler instanceof ActionSource2AttachedObjectHandler) ||
780                                 (currentTarget instanceof EditableValueHolderAttachedObjectTarget &&
781                                         currentHandler instanceof EditableValueHolderAttachedObjectHandler) ||
782                                 (currentTarget instanceof ValueHolderAttachedObjectTarget &&
783                                         currentHandler instanceof ValueHolderAttachedObjectHandler)))
784                 {
785                     // perf: getTargets return ArrayList - see getTargets implementations
786                     List<UIComponent> targets = currentTarget.getTargets(topLevelComponent);
787                     for (int l = 0, targetsCount = targets.size(); l < targetsCount; l++)
788                     {
789                         UIComponent component = targets.get(l);
790                         // If we found composite components when traverse the tree
791                         // we have to call this one recursively, because each composite component
792                         // should have its own AttachedObjectHandler list, filled earlier when
793                         // its tag handler is applied.
794                         if (UIComponent.isCompositeComponent(component))
795                         {
796                             // How we obtain the list of AttachedObjectHandler for
797                             // the current composite component? It should be a component
798                             // attribute or retrieved by a key inside component.getAttributes
799                             // map. Since api does not specify any attribute, we suppose
800                             // this is an implementation detail and it should be retrieved
801                             // from component attribute map.
802                             // But this is only the point of the iceberg, because we should
803                             // define how we register attached object handlers in this list.
804                             // ANS: see CompositeComponentResourceTagHandler.
805                             // The current handler should be added to the list, to be chained.
806                             // Note that the inner component should have a target with the same name
807                             // as "for" attribute
808                             mctx.addAttachedObjectHandler(component, currentHandler);
809 
810                             List<AttachedObjectHandler> handlers = mctx.getAttachedObjectHandlers(component);
811 
812                             retargetAttachedObjects(context, component, handlers);
813 
814                             handlers.remove(currentHandler);
815                         }
816                         else
817                         {
818                             currentHandler.applyAttachedObject(context, component);
819                         }
820                         if (mctx.isUsingPSSOnThisView() && mctx.isMarkInitialState())
821                         {
822                             component.markInitialState();
823                         }
824                     }
825                 }
826                 else if ((currentTarget instanceof BehaviorHolderAttachedObjectTarget &&
827                         currentHandler instanceof BehaviorHolderAttachedObjectHandler))
828                 {
829                     String eventName = ((BehaviorHolderAttachedObjectHandler) currentHandler).getEventName();
830                     boolean isDefaultEvent = ((BehaviorHolderAttachedObjectTarget) currentTarget).isDefaultEvent();
831 
832                     if ((eventName != null && eventName.equals(currentTarget.getName())) ||
833                             (eventName == null && isDefaultEvent))
834                     {
835                         List<UIComponent> targets = currentTarget.getTargets(topLevelComponent);
836                         for (int j = 0, targetssize = targets.size(); j < targetssize; j++)
837                         {
838                             UIComponent component = targets.get(j);
839                             // If we found composite components when traverse the tree
840                             // we have to call this one recursively, because each composite component
841                             // should have its own AttachedObjectHandler list, filled earlier when
842                             // its tag handler is applied.
843                             if (UIComponent.isCompositeComponent(component))
844                             {
845                                 if (currentTarget instanceof ClientBehaviorAttachedObjectTarget)
846                                 {
847                                     mctx.addAttachedObjectHandler(component,
848                                             new ClientBehaviorRedirectBehaviorAttachedObjectHandlerWrapper(
849                                                     (BehaviorHolderAttachedObjectHandler) currentHandler,
850                                                     ((ClientBehaviorAttachedObjectTarget) currentTarget).getEvent()));
851                                 }
852                                 else
853                                 {
854                                     mctx.addAttachedObjectHandler(component, currentHandler);
855                                 }
856 
857                                 List<AttachedObjectHandler> handlers = mctx.getAttachedObjectHandlers(component);
858 
859                                 retargetAttachedObjects(context, component, handlers);
860 
861                                 handlers.remove(currentHandler);
862                             }
863                             else
864                             {
865                                 if (currentHandler instanceof
866                                         ClientBehaviorRedirectBehaviorAttachedObjectHandlerWrapper)
867                                 {
868                                     currentHandler.applyAttachedObject(context,
869                                             new ClientBehaviorRedirectEventComponentWrapper(component,
870                                             ((ClientBehaviorRedirectBehaviorAttachedObjectHandlerWrapper)
871                                                     currentHandler).getWrappedEventName(), eventName));
872                                 }
873                                 else
874                                 {
875                                     currentHandler.applyAttachedObject(context, component);
876                                 }
877                             }
878                             if (mctx.isUsingPSSOnThisView() && mctx.isMarkInitialState())
879                             {
880                                 component.markInitialState();
881                             }
882                         }
883                     }
884                 }
885             }
886         }
887     }
888 
889     @Override
890     public void retargetMethodExpressions(FacesContext context, UIComponent topLevelComponent)
891     {
892         BeanInfo compositeComponentMetadata
893                 = (BeanInfo) topLevelComponent.getAttributes().get(UIComponent.BEANINFO_KEY);
894 
895         if (compositeComponentMetadata == null)
896         {
897             log.severe("Composite component metadata not found for: " + topLevelComponent.getClientId(context));
898             return;
899         }
900 
901         // "...For each attribute that is a MethodExpression..." This means we have to scan
902         // all attributes with "method-signature" attribute and no "type" attribute
903         // javax.faces.component._ComponentAttributesMap uses BeanInfo.getPropertyDescriptors to
904         // traverse over it, but here the metadata returned by UIComponent.BEANINFO_KEY is available
905         // only for composite components.
906         // That means somewhere we need to create a custom BeanInfo object for composite components
907         // that will be filled somewhere (theorically in ViewDeclarationLanguage.getComponentMetadata())
908 
909         PropertyDescriptor[] propertyDescriptors = compositeComponentMetadata.getPropertyDescriptors();
910 
911         ELContext elContext = (ELContext) context.getAttributes().get(FaceletContext.FACELET_CONTEXT_KEY);
912 
913         for (PropertyDescriptor propertyDescriptor : propertyDescriptors)
914         {
915             if (propertyDescriptor.getValue("type") != null)
916             {
917                 // This check is necessary if we have both "type" and "method-signature" set.
918                 // In that case, "method-signature" is ignored
919                 continue;
920             }
921 
922             String attributeName = propertyDescriptor.getName();
923             boolean isKnownMethod = "action".equals(attributeName) || "actionListener".equals(attributeName)  
924                     || "validator".equals(attributeName) || "valueChangeListener".equals(attributeName);
925 
926             // <composite:attribute> method-signature attribute is 
927             // ValueExpression that must evaluate to String
928             ValueExpression methodSignatureExpression
929                     = (ValueExpression) propertyDescriptor.getValue("method-signature");
930             String methodSignature = null;
931             if (methodSignatureExpression != null)
932             {
933                 // Check if the value expression holds a method signature
934                 // Note that it could be null, so in that case we don't have to do anything
935                 methodSignature = (String) methodSignatureExpression.getValue(elContext);
936             }
937 
938             // either the attributeName has to be a knownMethod or there has to be a method-signature
939             if (isKnownMethod || methodSignature != null)
940             {
941                 ValueExpression targetsExpression =
942                         (ValueExpression) propertyDescriptor.getValue("targets");
943 
944                 String targets = null;
945                 // <composite:attribute> targets attribute is 
946                 // ValueExpression that must evaluate to String
947                 if (targetsExpression != null)
948                 {
949                     targets = (String) targetsExpression.getValue(elContext);
950                 }
951 
952                 if (targets == null)
953                 {
954                     // "...let the name of the metadata element be the 
955                     // evaluated value of the targets attribute..."
956                     targets = attributeName;
957                 }
958 
959                 FaceletCompositionContext mctx = FaceletCompositionContext.getCurrentInstance();
960 
961                 // If the MethodExpression attribute has been already applied, there is no need to
962                 // handle it and it is probably a MethodExpression instance is on attribute map, so the
963                 // inner code will cause a ClassCastException.
964                 if (!mctx.isMethodExpressionAttributeApplied(topLevelComponent, attributeName))
965                 {
966 
967                     ValueExpression attributeNameValueExpression =
968                             (ValueExpression) topLevelComponent.getAttributes().get(attributeName);
969 
970                     if (attributeNameValueExpression == null)
971                     {
972                         // composite:attribute has a default property, so if we can't found on the
973                         // component attribute map, we should get the default as CompositeComponentELResolver
974                         // does.
975                         attributeNameValueExpression = (ValueExpression) propertyDescriptor.getValue("default");
976                         if (attributeNameValueExpression == null)
977                         {
978                             // It is only valid to log an error if the attribute is required
979                             ValueExpression ve = (ValueExpression) propertyDescriptor.getValue("required");
980                             if (ve != null)
981                             {
982                                 Object requiredValue = ve.getValue(elContext);
983                                 Boolean required = null;
984                                 if (requiredValue instanceof Boolean)
985                                 {
986                                     required = (Boolean) requiredValue;
987                                 }
988                                 else
989                                 {
990                                     required = Boolean.valueOf(requiredValue.toString());
991                                 }
992 
993                                 if (required != null && required.booleanValue())
994                                 {
995                                     if (log.isLoggable(Level.SEVERE))
996                                     {
997                                         log.severe("attributeValueExpression not found under the key \""
998                                                    + attributeName
999                                                    + "\". Looking for the next attribute");
1000                                     }
1001                                 }
1002                             }
1003                             continue;
1004                         }
1005                     }
1006 
1007                     String[] targetsArray = StringUtils.splitShortString(targets, ' ');
1008                     String attributeExpressionString = attributeNameValueExpression.getExpressionString();
1009                     MethodExpression methodExpression = null;
1010                     MethodExpression methodExpression2 = null;
1011 
1012                     //Check if the stored valueExpression is a ccRedirection, to handle it properly later.
1013                     boolean ccAttrMeRedirection =
1014                             attributeNameValueExpression instanceof LocationValueExpression &&
1015                                     CompositeComponentELUtils.isCompositeComponentAttrsMethodExpression(
1016                                             attributeNameValueExpression.getExpressionString());
1017 
1018                     if (isKnownMethod)
1019                     {
1020                         // To add support to #{cc.attrs.action}, #{cc.attrs.actionListener}, #{cc.attrs.validator} or
1021                         // #{cc.attrs.valueChangeListener} it is necessary to put a MethodExpression or a 
1022                         // ValueExpression pointing to the associated java method in the component attribute map.
1023                         // org.apache.myfaces.view.facelets.tag.composite.RetargetMethodExpressionRule already put
1024                         // a ValueExpression, so we only need to put a MethodExpression when a non redirecting
1025                         // expression is used (for example when a nested #{cc.attrs.xxx} is used).
1026                         if ("action".equals(attributeName))
1027                         {
1028                             // target is ActionSource2
1029                             methodExpression = reWrapMethodExpression(context.getApplication().getExpressionFactory().
1030                                     createMethodExpression(elContext,
1031                                             attributeExpressionString, null, EMPTY_CLASS_ARRAY), attributeNameValueExpression);
1032 
1033                             //Store the method expression to the topLevelComponent to allow reference it through EL
1034                             if (!ccAttrMeRedirection)
1035                             {
1036                                 //Replace it with a method expression
1037                                 topLevelComponent.getAttributes().put(attributeName, methodExpression);
1038                             }
1039                             // Otherwise keep the current ValueExpression, because it will be used chain other value expressions
1040                         }
1041                         else if ("actionListener".equals(attributeName))
1042                         {
1043                             // target is ActionSource2
1044                             methodExpression = reWrapMethodExpression(context.getApplication().getExpressionFactory().
1045                                     createMethodExpression(elContext,
1046                                             attributeExpressionString, Void.TYPE, ACTION_LISTENER_SIGNATURE), attributeNameValueExpression);
1047 
1048                             methodExpression2 = reWrapMethodExpression(context.getApplication().getExpressionFactory().
1049                                     createMethodExpression(elContext,
1050                                             attributeExpressionString, Void.TYPE, EMPTY_CLASS_ARRAY), attributeNameValueExpression);
1051 
1052                             //Store the method expression to the topLevelComponent to allow reference it through EL
1053                             if (!ccAttrMeRedirection)
1054                             {
1055                                 //Replace it with a method expression
1056                                 topLevelComponent.getAttributes().put(attributeName, new MethodExpressionMethodExpression(methodExpression, methodExpression2));
1057                             }
1058                             // Otherwise keep the current ValueExpression, because it will be used chain other value expressions
1059                         }
1060                         else if ("validator".equals(attributeName))
1061                         {
1062                             // target is EditableValueHolder
1063                             methodExpression = reWrapMethodExpression(context.getApplication().getExpressionFactory().
1064                                     createMethodExpression(elContext,
1065                                         attributeExpressionString, Void.TYPE, 
1066                                         VALIDATOR_SIGNATURE), attributeNameValueExpression);
1067 
1068                             //Store the method expression to the topLevelComponent to allow reference it through EL
1069                             if (!ccAttrMeRedirection)
1070                             {
1071                                 //Replace it with a method expression
1072                                 topLevelComponent.getAttributes().put(attributeName, methodExpression);
1073                             }
1074                             // Otherwise keep the current ValueExpression, because it will be used chain other value expressions
1075                         }
1076                         else if ("valueChangeListener".equals(attributeName))
1077                         {
1078                             // target is EditableValueHolder
1079                             methodExpression = reWrapMethodExpression(context.getApplication().getExpressionFactory().
1080                                     createMethodExpression(elContext,
1081                                             attributeExpressionString, Void.TYPE, 
1082                                             VALUE_CHANGE_LISTENER_SIGNATURE), attributeNameValueExpression);
1083 
1084                             methodExpression2 = reWrapMethodExpression(context.getApplication().getExpressionFactory().
1085                                     createMethodExpression(elContext,
1086                                             attributeExpressionString, Void.TYPE, 
1087                                             EMPTY_CLASS_ARRAY), attributeNameValueExpression);
1088 
1089                             //Store the method expression to the topLevelComponent to allow reference it through EL
1090                             if (!ccAttrMeRedirection)
1091                             {
1092                                 //Replace it with a method expression
1093                                 topLevelComponent.getAttributes().put(attributeName, new MethodExpressionMethodExpression(methodExpression, methodExpression2));
1094                             }
1095                             // Otherwise keep the current ValueExpression, because it will be used chain other value expressions
1096                         }
1097 
1098                         UIComponent topLevelComponentBase = topLevelComponent.getFacet(UIComponent.COMPOSITE_FACET_NAME);
1099 
1100                         for (String target : targetsArray)
1101                         {
1102                             UIComponent innerComponent = ComponentSupport.findComponentChildOrFacetFrom(context, topLevelComponentBase, target);
1103 
1104                             if (innerComponent == null)
1105                             {
1106                                 //if (log.isLoggable(Level.SEVERE))
1107                                 //    log.severe("Inner component " + target + " not found when retargetMethodExpressions");
1108                                 continue;
1109                             }
1110 
1111                             if (isCompositeComponentRetarget(context, innerComponent, attributeName))
1112                             {
1113                                 innerComponent.getAttributes().put(attributeName, attributeNameValueExpression);
1114 
1115                                 mctx.clearMethodExpressionAttribute(innerComponent, attributeName);
1116 
1117                                 retargetMethodExpressions(context, innerComponent);
1118                                 if (mctx.isUsingPSSOnThisView() && mctx.isMarkInitialState())
1119                                 {
1120                                     innerComponent.markInitialState();
1121                                 }
1122                             }
1123                             else
1124                             {
1125                                 if ("action".equals(attributeName))
1126                                 {
1127                                     // target is ActionSource2
1128                                     methodExpression = reWrapMethodExpression(context.getApplication().getExpressionFactory().
1129                                             createMethodExpression(elContext,
1130                                                     attributeExpressionString, null, EMPTY_CLASS_ARRAY), attributeNameValueExpression);
1131 
1132                                     // If it is a redirection, a wrapper is used to locate the right instance and call it properly. 
1133                                     if (ccAttrMeRedirection)
1134                                     {
1135                                         ((ActionSource2)innerComponent).setActionExpression(new ValueExpressionMethodExpression(attributeNameValueExpression));
1136                                     }
1137                                     else
1138                                     {
1139                                         ((ActionSource2)innerComponent).setActionExpression(methodExpression);
1140                                     }
1141                                     if (mctx.isUsingPSSOnThisView() && mctx.isMarkInitialState())
1142                                     {
1143                                         innerComponent.markInitialState();
1144                                     }
1145                                 }
1146                                 else if ("actionListener".equals(attributeName))
1147                                 {
1148                                     //First try to remove any prevous target if any
1149                                     ActionListener o = (ActionListener) mctx.removeMethodExpressionTargeted(innerComponent, attributeName);
1150                                     if (o != null)
1151                                     {
1152                                         ((ActionSource2) innerComponent).removeActionListener(o);
1153                                     }
1154 
1155                                     // target is ActionSource2
1156                                     ActionListener actionListener = null;
1157                                     // If it is a redirection, a wrapper is used to locate the right instance and call it properly.
1158                                     if (ccAttrMeRedirection)
1159                                     {
1160                                         actionListener = new RedirectMethodExpressionValueExpressionActionListener(attributeNameValueExpression);
1161                                     }
1162                                     else
1163                                     {
1164                                         methodExpression = reWrapMethodExpression(context.getApplication().getExpressionFactory().
1165                                                 createMethodExpression(elContext,
1166                                                         attributeExpressionString, Void.TYPE, ACTION_LISTENER_SIGNATURE), attributeNameValueExpression);
1167 
1168                                         methodExpression2 = reWrapMethodExpression(context.getApplication().getExpressionFactory().
1169                                                 createMethodExpression(elContext,
1170                                                         attributeExpressionString, Void.TYPE, EMPTY_CLASS_ARRAY), attributeNameValueExpression);
1171 
1172                                         if (mctx.isUsingPSSOnThisView())
1173                                         {
1174                                             actionListener = new PartialMethodExpressionActionListener(methodExpression, methodExpression2);
1175                                         }
1176                                         else
1177                                         {
1178                                             actionListener = new MethodExpressionActionListener(methodExpression, methodExpression2);
1179                                         }
1180                                     }
1181                                     ((ActionSource2) innerComponent).addActionListener(actionListener);
1182                                     mctx.addMethodExpressionTargeted(innerComponent, attributeName, actionListener);
1183                                     if (mctx.isUsingPSSOnThisView() && mctx.isMarkInitialState())
1184                                     {
1185                                         innerComponent.markInitialState();
1186                                     }
1187                                 }
1188                                 else if ("validator".equals(attributeName))
1189                                 {
1190                                     //First try to remove any prevous target if any
1191                                     Validator o = (Validator) mctx.removeMethodExpressionTargeted(innerComponent, attributeName);
1192                                     if (o != null)
1193                                     {
1194                                         ((EditableValueHolder) innerComponent).removeValidator(o);
1195                                     }
1196 
1197                                     // target is EditableValueHolder
1198                                     Validator validator = null;
1199                                     // If it is a redirection, a wrapper is used to locate the right instance and call it properly.
1200                                     if (ccAttrMeRedirection)
1201                                     {
1202                                         validator = new RedirectMethodExpressionValueExpressionValidator(attributeNameValueExpression);
1203                                     }
1204                                     else
1205                                     {
1206                                         methodExpression = reWrapMethodExpression(context.getApplication().getExpressionFactory().
1207                                                 createMethodExpression(elContext,
1208                                                         attributeExpressionString, Void.TYPE,
1209                                                         VALIDATOR_SIGNATURE), attributeNameValueExpression);
1210                                         if (mctx.isUsingPSSOnThisView())
1211                                         {
1212                                             validator = new PartialMethodExpressionValidator(methodExpression);
1213                                         }
1214                                         else
1215                                         {
1216                                             validator = new MethodExpressionValidator(methodExpression);
1217                                         }
1218                                     }
1219                                     ((EditableValueHolder) innerComponent).addValidator(validator);
1220                                     mctx.addMethodExpressionTargeted(innerComponent, attributeName, validator);
1221                                     if (mctx.isUsingPSSOnThisView() && mctx.isMarkInitialState())
1222                                     {
1223                                         innerComponent.markInitialState();
1224                                     }
1225                                 }
1226                                 else if ("valueChangeListener".equals(attributeName))
1227                                 {
1228                                     ValueChangeListener o = (ValueChangeListener) mctx.removeMethodExpressionTargeted(innerComponent, attributeName);
1229                                     if (o != null)
1230                                     {
1231                                         ((EditableValueHolder)innerComponent).removeValueChangeListener(o);
1232                                     }
1233 
1234                                     // target is EditableValueHolder
1235                                     ValueChangeListener valueChangeListener = null;
1236                                     // If it is a redirection, a wrapper is used to locate the right instance and call it properly.
1237                                     if (ccAttrMeRedirection)
1238                                     {
1239                                         valueChangeListener = new RedirectMethodExpressionValueExpressionValueChangeListener(attributeNameValueExpression);
1240                                     }
1241                                     else
1242                                     {
1243                                         methodExpression = reWrapMethodExpression(context.getApplication().getExpressionFactory().
1244                                                 createMethodExpression(elContext,
1245                                                         attributeExpressionString, Void.TYPE,
1246                                                         VALUE_CHANGE_LISTENER_SIGNATURE), attributeNameValueExpression);
1247 
1248                                         methodExpression2 = reWrapMethodExpression(context.getApplication().getExpressionFactory().
1249                                                 createMethodExpression(elContext,
1250                                                         attributeExpressionString, Void.TYPE,
1251                                                         EMPTY_CLASS_ARRAY), attributeNameValueExpression);
1252 
1253                                         if (mctx.isUsingPSSOnThisView())
1254                                         {
1255                                             valueChangeListener = new PartialMethodExpressionValueChangeListener(methodExpression, methodExpression2);
1256                                         }
1257                                         else
1258                                         {
1259                                             valueChangeListener = new MethodExpressionValueChangeListener(methodExpression, methodExpression2);
1260                                         }
1261                                     }
1262                                     ((EditableValueHolder) innerComponent).addValueChangeListener(valueChangeListener);
1263                                     mctx.addMethodExpressionTargeted(innerComponent, attributeName, valueChangeListener);
1264                                     if (mctx.isUsingPSSOnThisView() && mctx.isMarkInitialState())
1265                                     {
1266                                         innerComponent.markInitialState();
1267                                     }
1268                                 }
1269                             }
1270                         }
1271                     }
1272                     else
1273                     {
1274                         // composite:attribute targets property only has sense for action, actionListener,
1275                         // validator or valueChangeListener. This means we have to retarget the method expression
1276                         // to the topLevelComponent.
1277 
1278                         // Since a MethodExpression has no state, we can use it multiple times without problem, so
1279                         // first create it here.
1280                         methodSignature = methodSignature.trim();
1281                         methodExpression = context.getApplication().getExpressionFactory().
1282                                 createMethodExpression(elContext,
1283                                         attributeExpressionString, _getReturnType(methodSignature), 
1284                                         _getParameters(methodSignature));
1285 
1286                         methodExpression = reWrapMethodExpression(methodExpression, attributeNameValueExpression);
1287 
1288                         UIComponent topLevelComponentBase = topLevelComponent.getFacet(UIComponent.COMPOSITE_FACET_NAME);
1289 
1290                         for (String target : targetsArray)
1291                         {
1292                             UIComponent innerComponent = ComponentSupport.findComponentChildOrFacetFrom(context, topLevelComponentBase, target);
1293 
1294                             if (innerComponent == null)
1295                             {
1296                                 continue;
1297                             }
1298 
1299                             // If a component is found, that means the expression should be retarget to the
1300                             // components related
1301                             if (isCompositeComponentRetarget(context, innerComponent, attributeName))
1302                             {
1303                                 innerComponent.getAttributes().put(attributeName, attributeNameValueExpression);
1304 
1305                                 mctx.clearMethodExpressionAttribute(innerComponent, attributeName);
1306 
1307                                 retargetMethodExpressions(context, innerComponent);
1308                                 if (mctx.isUsingPSSOnThisView() && mctx.isMarkInitialState())
1309                                 {
1310                                     innerComponent.markInitialState();
1311                                 }
1312                             }
1313                             else
1314                             {
1315                                 //Put the retarget
1316                                 if (ccAttrMeRedirection)
1317                                 {
1318                                     // Since we require here a method expression, it is necessary to wrap the ValueExpression
1319                                     // into a MethodExpression that handles redirection.
1320                                     innerComponent.getAttributes().put(attributeName, new ValueExpressionMethodExpression(attributeNameValueExpression));
1321                                 }
1322                                 else
1323                                 {
1324                                     innerComponent.getAttributes().put(attributeName, methodExpression);
1325                                 }
1326                                 if (mctx.isUsingPSSOnThisView() && mctx.isMarkInitialState())
1327                                 {
1328                                     innerComponent.markInitialState();
1329                                 }
1330                             }
1331                         }
1332                         //Store the method expression to the topLevelComponent to allow reference it through EL
1333                         if (!ccAttrMeRedirection)
1334                         {
1335                             //Replace it with a method expression
1336                             topLevelComponent.getAttributes().put(attributeName, methodExpression);
1337                         }
1338                         // Othewise keep the current ValueExpression, because it will be used chain other value expressions
1339                     }
1340                     mctx.markMethodExpressionAttribute(topLevelComponent, attributeName);
1341                 }
1342 
1343                 // We need to remove the previous ValueExpression, to prevent some possible
1344                 // confusion when the same value is retrieved from the attribute map.
1345                 topLevelComponent.setValueExpression(attributeName, null);
1346             }
1347         }
1348     }
1349 
1350     private boolean isCompositeComponentRetarget(FacesContext context, UIComponent component, String attributeName)
1351     {
1352         if (UIComponent.isCompositeComponent(component))
1353         {
1354             BeanInfo compositeComponentMetadata = (BeanInfo) component.getAttributes().get(UIComponent.BEANINFO_KEY);
1355 
1356             PropertyDescriptor[] propertyDescriptors = compositeComponentMetadata.getPropertyDescriptors();
1357 
1358             ELContext elContext = (ELContext) context.getAttributes().get(FaceletContext.FACELET_CONTEXT_KEY);
1359 
1360             for (PropertyDescriptor propertyDescriptor : propertyDescriptors)
1361             {
1362                 if (propertyDescriptor.getValue("type") != null)
1363                 {
1364                     // This check is necessary if we have both "type" and "method-signature" set.
1365                     // In that case, "method-signature" is ignored
1366                     continue;
1367                 }
1368 
1369                 if (attributeName.equals(propertyDescriptor.getName()))
1370                 {
1371                     boolean isKnownMethod = "action".equals(attributeName) || "actionListener".equals(attributeName)  
1372                     || "validator".equals(attributeName) || "valueChangeListener".equals(attributeName);
1373 
1374                     // <composite:attribute> method-signature attribute is 
1375                     // ValueExpression that must evaluate to String
1376                     ValueExpression methodSignatureExpression
1377                             = (ValueExpression) propertyDescriptor.getValue("method-signature");
1378                     String methodSignature = null;
1379                     if (methodSignatureExpression != null)
1380                     {
1381                         // Check if the value expression holds a method signature
1382                         // Note that it could be null, so in that case we don't have to do anything
1383                         methodSignature = (String) methodSignatureExpression.getValue(elContext);
1384                     }
1385 
1386                     // either the attributeName has to be a knownMethod or there has to be a method-signature
1387                     if (isKnownMethod || methodSignature != null)
1388                     {
1389                         if ("action".equals(attributeName))
1390                         {
1391                             return !(component instanceof ActionSource2);
1392                         }
1393                         else if ("actionListener".equals(attributeName))
1394                         {
1395                             return !(component instanceof ActionSource2);
1396                         }
1397                         else if ("validator".equals(attributeName))
1398                         {
1399                             return !(component instanceof EditableValueHolder);
1400                         }
1401                         else if ("valueChangeListener".equals(attributeName))
1402                         {
1403                             return !(component instanceof EditableValueHolder);
1404                         }
1405                         else
1406                         {
1407                             return true;
1408                         }
1409                     }
1410                 }
1411             }
1412             return false;
1413         }
1414         else
1415         {
1416             return false;
1417         }
1418     }
1419 
1420     @SuppressWarnings("unchecked")
1421     private MethodExpression reWrapMethodExpression(MethodExpression createdMethodExpression,
1422                                                     ValueExpression originalValueExpression)
1423     {
1424         if (originalValueExpression instanceof LocationValueExpression)
1425         {
1426             return new LocationMethodExpression(
1427                     ((LocationValueExpression) originalValueExpression).getLocation(),
1428                     reWrapMethodExpression(createdMethodExpression,
1429                             ((LocationValueExpression) originalValueExpression).getWrapped()),
1430                     ((LocationValueExpression) originalValueExpression).getCCLevel());
1431         }
1432         else if (originalValueExpression instanceof FacesWrapper &&
1433                 ((FacesWrapper) originalValueExpression).getWrapped() instanceof ValueExpression)
1434         {
1435             return reWrapMethodExpression(createdMethodExpression,
1436                     (ValueExpression) ((FacesWrapper) originalValueExpression).getWrapped());
1437         }
1438         else
1439         {
1440             return createdMethodExpression;
1441         }
1442     }
1443 
1444     /**
1445      * This method is similar to shared ClassUtils.javaTypeToClass,
1446      * but the default package is java.lang
1447      * TODO: Move to shared project
1448      *
1449      * @param type
1450      * @return
1451      * @throws ClassNotFoundException
1452      */
1453     public static Class _javaTypeToClass(String type)
1454             throws ClassNotFoundException
1455     {
1456         if (type == null)
1457         {
1458             throw new NullPointerException("type");
1459         }
1460 
1461         // try common types and arrays of common types first
1462         Class clazz = (Class) ClassUtils.COMMON_TYPES.get(type);
1463         if (clazz != null)
1464         {
1465             return clazz;
1466         }
1467 
1468         int len = type.length();
1469         if (len > 2 && type.charAt(len - 1) == ']' && type.charAt(len - 2) == '[')
1470         {
1471             String componentType = type.substring(0, len - 2);
1472             Class componentTypeClass = ClassUtils.classForName(componentType);
1473             return Array.newInstance(componentTypeClass, 0).getClass();
1474         }
1475 
1476         if (type.indexOf('.') == -1)
1477         {
1478             type = "java.lang." + type;
1479         }
1480         return ClassUtils.classForName(type);
1481     }
1482 
1483     private Class _getReturnType(String signature)
1484     {
1485         int endName = signature.indexOf('(');
1486         if (endName < 0)
1487         {
1488             throw new FacesException("Invalid method signature:" + signature);
1489         }
1490         int end = signature.lastIndexOf(' ', endName);
1491         if (end < 0)
1492         {
1493             throw new FacesException("Invalid method signature:" + signature);
1494         }
1495         try
1496         {
1497             return _javaTypeToClass(signature.substring(0, end));
1498         }
1499         catch (ClassNotFoundException e)
1500         {
1501             throw new FacesException("Invalid method signature:" + signature);
1502         }
1503     }
1504 
1505     /**
1506      * Get the parameters types from the function signature.
1507      *
1508      * @return An array of parameter class names
1509      */
1510     private Class[] _getParameters(String signature) throws FacesException
1511     {
1512         ArrayList<Class> params = new ArrayList<Class>();
1513         // Signature is of the form
1514         // <return-type> S <method-name S? '('
1515         // < <arg-type> ( ',' <arg-type> )* )? ')'
1516         int start = signature.indexOf('(') + 1;
1517         boolean lastArg = false;
1518         while (true)
1519         {
1520             int p = signature.indexOf(',', start);
1521             if (p < 0)
1522             {
1523                 p = signature.indexOf(')', start);
1524                 if (p < 0)
1525                 {
1526                     throw new FacesException("Invalid method signature:" + signature);
1527                 }
1528                 lastArg = true;
1529             }
1530             String arg = signature.substring(start, p).trim();
1531             if (!"".equals(arg))
1532             {
1533                 try
1534                 {
1535                     params.add(_javaTypeToClass(arg));
1536                 }
1537                 catch (ClassNotFoundException e)
1538                 {
1539                     throw new FacesException("Invalid method signature:" + signature);
1540                 }
1541             }
1542             if (lastArg)
1543             {
1544                 break;
1545             }
1546             start = p + 1;
1547         }
1548         return params.toArray(new Class[params.size()]);
1549     }
1550 
1551     /**
1552      * {@inheritDoc}
1553      */
1554     @Override
1555     public Resource getScriptComponentResource(FacesContext context, Resource componentResource)
1556     {
1557         // TODO Auto-generated method stub
1558         return null;
1559     }
1560 
1561     /**
1562      * {@inheritDoc}
1563      */
1564     @Override
1565     public StateManagementStrategy getStateManagementStrategy(FacesContext context, String viewId)
1566     {
1567         // Use partial state saving strategy only if javax.faces.PARTIAL_STATE_SAVING is "true" and
1568         // the current view is not on javax.faces.FULL_STATE_SAVING_VIEW_IDS.
1569         if (_partialStateSaving && _stateMgmtStrategy == null)
1570         {
1571             _stateMgmtStrategy = new DefaultFaceletsStateManagementStrategy();
1572         }
1573 
1574         return _usePartialStateSavingOnThisView(viewId) ? _stateMgmtStrategy : null;
1575     }
1576 
1577     /**
1578      * {@inheritDoc}
1579      */
1580     @Override
1581     public ViewMetadata getViewMetadata(FacesContext context, String viewId)
1582     {
1583         return new FaceletViewMetadata(viewId);
1584     }
1585 
1586     /**
1587      * {@inheritDoc}
1588      */
1589     @Override
1590     public void renderView(FacesContext context, UIViewRoot view) throws IOException
1591     {
1592         if (!view.isRendered())
1593         {
1594             return;
1595         }
1596 
1597         // log request
1598         if (log.isLoggable(Level.FINE))
1599         {
1600             log.fine("Rendering View: " + view.getViewId());
1601         }
1602 
1603         try
1604         {
1605             // build view - but not if we're in "buildBeforeRestore"
1606             // land and we've already got a populated view. Note
1607             // that this optimizations breaks if there's a "c:if" in
1608             // the page that toggles as a result of request processing -
1609             // should that be handled? Or
1610             // is this optimization simply so minor that it should just
1611             // be trimmed altogether?
1612             // See JSF 2.0 spec section 2.2.6, buildView is called before
1613             // Render Response
1614             //if (!isFilledView(context, view))
1615             //{
1616             //    buildView(context, view);
1617             //}
1618 
1619             // setup writer and assign it to the context
1620             ResponseWriter origWriter = createResponseWriter(context);
1621 
1622             ExternalContext extContext = context.getExternalContext();
1623             Writer outputWriter = extContext.getResponseOutputWriter();
1624 
1625             StateWriter stateWriter = new StateWriter(outputWriter, 1024, context);
1626             try
1627             {
1628                 ResponseWriter writer = origWriter.cloneWithWriter(stateWriter);
1629                 try
1630                 {
1631                     context.setResponseWriter(writer);
1632 
1633                     StateManager stateMgr = context.getApplication().getStateManager();
1634                     // force creation of session if saving state there
1635                     // -= Leonardo Uribe =- Do this does not have any sense!. The only reference
1636                     // about these lines are on http://java.net/projects/facelets/sources/svn/revision/376
1637                     // and it says: "fixed lazy session instantiation with eager response commit"
1638                     // This code is obviously to prevent this exception:
1639                     // java.lang.IllegalStateException: Cannot create a session after the response has been committed
1640                     // But in theory if that so, StateManager.saveState must happen before writer.close() is called,
1641                     // which can be done very easily.
1642                     //if (!stateMgr.isSavingStateInClient(context))
1643                     //{
1644                     //    extContext.getSession(true);
1645                     //}
1646 
1647                     // render the view to the response
1648                     writer.startDocument();
1649 
1650                     view.encodeAll(context);
1651 
1652                     writer.endDocument();
1653 
1654                     // finish writing
1655                     // -= Leonardo Uribe =- This does not has sense too, because that's the reason
1656                     // of the try/finally block. In practice, it only forces the close of the tag 
1657                     // in HtmlResponseWriter if necessary, but according to the spec, this should
1658                     // be done using writer.flush() instead.
1659                     // writer.close();
1660 
1661                     // flush to origWriter
1662                     if (stateWriter.isStateWritten())
1663                     {
1664                         // Call this method to force close the tag if necessary.
1665                         // The spec javadoc says this: 
1666                         // "... Flush any ouput buffered by the output method to the underlying 
1667                         // Writer or OutputStream. This method will not flush the underlying 
1668                         // Writer or OutputStream; it simply clears any values buffered by this 
1669                         // ResponseWriter. ..."
1670                         writer.flush();
1671 
1672                         // =-= markoc: STATE_KEY is in output ONLY if 
1673                         // stateManager.isSavingStateInClient(context)is true - see
1674                         // org.apache.myfaces.application.ViewHandlerImpl.writeState(FacesContext)
1675                         // TODO this class and ViewHandlerImpl contain same constant <!--@@JSF_FORM_STATE_MARKER@@-->
1676                         Object stateObj = stateMgr.saveView(context);
1677                         String content = stateWriter.getAndResetBuffer();
1678                         int end = content.indexOf(STATE_KEY);
1679                         // See if we can find any trace of the saved state.
1680                         // If so, we need to perform token replacement
1681                         if (end >= 0)
1682                         {
1683                             // save state
1684                             String stateStr;
1685                             if (stateObj == null)
1686                             {
1687                                 stateStr = null;
1688                             }
1689                             else
1690                             {
1691                                 stateMgr.writeState(context, stateObj);
1692                                 stateStr = stateWriter.getAndResetBuffer();
1693                             }
1694 
1695                             int start = 0;
1696 
1697                             while (end != -1)
1698                             {
1699                                 origWriter.write(content, start, end - start);
1700                                 if (stateStr != null)
1701                                 {
1702                                     origWriter.write(stateStr);
1703                                 }
1704                                 start = end + STATE_KEY_LEN;
1705                                 end = content.indexOf(STATE_KEY, start);
1706                             }
1707 
1708                             origWriter.write(content, start, content.length() - start);
1709                             // No trace of any saved state, so we just need to flush
1710                             // the buffer
1711                         }
1712                         else
1713                         {
1714                             origWriter.write(content);
1715                         }
1716                     }
1717                     else if (stateWriter.isStateWrittenWithoutWrapper())
1718                     {
1719                         // The state token has been written but the state has not been
1720                         // saved yet.
1721                         stateMgr.saveView(context);
1722                     }
1723                 }
1724                 finally
1725                 {
1726                     // The Facelets implementation must close the writer used to write the response
1727                     writer.close();
1728                 }
1729             }
1730             finally
1731             {
1732                 stateWriter.release(context);
1733             }
1734         }
1735         catch (FileNotFoundException fnfe)
1736         {
1737             handleFaceletNotFound(context, view.getViewId());
1738         }
1739         catch (Exception e)
1740         {
1741             handleRenderException(context, e);
1742         }
1743     }
1744 
1745     /**
1746      * {@inheritDoc}
1747      */
1748     @Override
1749     public UIViewRoot createView(FacesContext context, String viewId)
1750     {
1751         // we have to check for a possible debug request
1752         if (UIDebug.debugRequest(context))
1753         {
1754             // the current request is a debug request, so we don't need
1755             // to create a view, since the output has already been written
1756             // in UIDebug.debugRequest() and facesContext.responseComplete()
1757             // has been called.
1758             return null;
1759         }
1760         else
1761         {
1762             return super.createView(context, viewId);
1763         }
1764     }
1765 
1766     /**
1767      * {@inheritDoc}
1768      */
1769     @Override
1770     public UIViewRoot restoreView(FacesContext context, String viewId)
1771     {
1772         // Currently there is no way, in which UIDebug.debugRequest(context)
1773         // can create debug information and return true at this point,
1774         // because this method is only accessed if the current request
1775         // is a postback, which will never be true for a debug page.
1776         // The only point where valid debug output can be produced by now
1777         // is in createView() -= Jakob Korherr =-
1778         //if (UIDebug.debugRequest(context))
1779         //{
1780         //    return new UIViewRoot();
1781         //}
1782 
1783         //else if (!_buildBeforeRestore)
1784         //{
1785         return super.restoreView(context, viewId);
1786         //}
1787         //else
1788         //{
1789         // TODO: VALIDATE - Is _buildBeforeRestore relevant at all for 2.0? -= SL =-
1790         // ANS: buildBeforeRestore evolved to partial state saving, so this logic
1791         // is now on StateManagerStrategy implementation -= Leo U =-
1792         /*
1793             UIViewRoot viewRoot = createView(context, viewId);
1794 
1795             context.setViewRoot(viewRoot);
1796 
1797             try
1798             {
1799                 buildView(context, viewRoot);
1800             }
1801             catch (IOException ioe)
1802             {
1803                 log.severe("Error Building View", ioe);
1804             }
1805 
1806             Application application = context.getApplication();
1807 
1808             ViewHandler applicationViewHandler = application.getViewHandler();
1809 
1810             String renderKitId = applicationViewHandler.calculateRenderKitId(context);
1811 
1812             application.getStateManager().restoreView(context, viewId, renderKitId);
1813 
1814             return viewRoot;
1815         }
1816         */
1817     }
1818 
1819     /**
1820      * {@inheritDoc}
1821      */
1822     @Override
1823     protected String calculateViewId(FacesContext context, String viewId)
1824     {
1825         if (_cachedViewHandlerSupport == null)
1826         {
1827             _cachedViewHandlerSupport = new DefaultViewHandlerSupport();
1828         }
1829 
1830         return _cachedViewHandlerSupport.calculateViewId(context, viewId);
1831     }
1832 
1833     /**
1834      * Creates the Facelet page compiler.
1835      *
1836      * @param context
1837      *            the current FacesContext
1838      *
1839      * @return the application's Facelet page compiler
1840      */
1841     protected Compiler createCompiler(FacesContext context)
1842     {
1843         Compiler compiler = new SAXCompiler();
1844 
1845         compiler.setDevelopmentProjectStage(context.isProjectStage(ProjectStage.Development));
1846 
1847         loadLibraries(context, compiler);
1848         loadDecorators(context, compiler);
1849         loadOptions(context, compiler);
1850 
1851         return compiler;
1852     }
1853 
1854     /**
1855      * Creates a FaceletFactory instance using the specified compiler.
1856      *
1857      * @param context
1858      *            the current FacesContext
1859      * @param compiler
1860      *            the compiler to be used by the factory
1861      *
1862      * @return the factory used by this VDL to load pages
1863      */
1864     protected FaceletFactory createFaceletFactory(FacesContext context, Compiler compiler)
1865     {
1866         ExternalContext eContext = context.getExternalContext();
1867 
1868         // refresh period
1869         long refreshPeriod;
1870         if (context.isProjectStage(ProjectStage.Production))
1871         {
1872             refreshPeriod = WebConfigParamUtils.getLongInitParameter(eContext, PARAMS_REFRESH_PERIOD,
1873                     DEFAULT_REFRESH_PERIOD_PRODUCTION);
1874         }
1875         else
1876         {
1877             refreshPeriod = WebConfigParamUtils.getLongInitParameter(eContext, PARAMS_REFRESH_PERIOD,
1878                     DEFAULT_REFRESH_PERIOD);
1879         }
1880 
1881         // resource resolver
1882         ResourceResolver resolver = new DefaultResourceResolver();
1883         String faceletsResourceResolverClassName = WebConfigParamUtils.getStringInitParameter(eContext,
1884                 PARAMS_RESOURCE_RESOLVER, null);
1885         if (faceletsResourceResolverClassName != null)
1886         {
1887             ArrayList<String> classNames = new ArrayList<String>(1);
1888             classNames.add(faceletsResourceResolverClassName);
1889             resolver = ClassUtils.buildApplicationObject(ResourceResolver.class, classNames, resolver);
1890         }
1891 
1892         return new DefaultFaceletFactory(compiler, resolver, refreshPeriod);
1893     }
1894 
1895 
1896     protected ResponseWriter createResponseWriter(FacesContext context) throws IOException, FacesException
1897     {
1898         ExternalContext extContext = context.getExternalContext();
1899         RenderKit renderKit = context.getRenderKit();
1900         // Avoid a cryptic NullPointerException when the renderkit ID
1901         // is incorrectly set
1902         if (renderKit == null)
1903         {
1904             String id = context.getViewRoot().getRenderKitId();
1905             throw new IllegalStateException("No render kit was available for id \"" + id + "\"");
1906         }
1907 
1908         // set the buffer for content
1909         if (_bufferSize != -1)
1910         {
1911             extContext.setResponseBufferSize(_bufferSize);
1912         }
1913 
1914         // get our content type
1915         String contentType = (String) context.getAttributes().get("facelets.ContentType");
1916 
1917         // get the encoding
1918         String encoding = (String) context.getAttributes().get("facelets.Encoding");
1919 
1920         // -= Leonardo Uribe =- Add */* to the contentType is a fix done from FaceletViewHandler
1921         // to make old RI versions work, but since this is for JSF 2.0 it is not necessary that code.
1922         ResponseWriter writer = renderKit.createResponseWriter(NullWriter.Instance, contentType, encoding);
1923 
1924         //ResponseWriter writer;
1925         // append */* to the contentType so createResponseWriter will succeed no matter
1926         // the requested contentType.
1927         //if (contentType != null && !contentType.equals("*/*"))
1928         //{
1929         //    contentType += ",*/*";
1930         //}
1931         // Create a dummy ResponseWriter with a bogus writer,
1932         // so we can figure out what content type the ReponseWriter
1933         // is really going to ask for
1934         //try
1935         //{
1936         //    writer = renderKit.createResponseWriter(NullWriter.Instance, contentType, encoding);
1937         //}
1938         // catch (IllegalArgumentException e)
1939         //{
1940         // Added because of an RI bug prior to 1.2_05-b3. Might as well leave it in case other
1941         // impls have the same problem. https://javaserverfaces.dev.java.net/issues/show_bug.cgi?id=613
1942         //log.finest("The impl didn't correctly handled '*/*' in the content type list.  Trying '*/*' directly.");
1943         //writer = renderKit.createResponseWriter(NullWriter.Instance, "*/*", encoding);
1944         //}
1945 
1946         // Override the JSF provided content type if necessary
1947         contentType = getResponseContentType(context, writer.getContentType());
1948         encoding = getResponseEncoding(context, writer.getCharacterEncoding());
1949 
1950         // apply them to the response
1951         extContext.setResponseContentType(contentType + "; charset=" + encoding);
1952 
1953         // removed 2005.8.23 to comply with J2EE 1.3
1954         // response.setCharacterEncoding(encoding);
1955 
1956         // Now, clone with the real writer
1957         writer = writer.cloneWithWriter(extContext.getResponseOutputWriter());
1958 
1959         return writer;
1960     }
1961 
1962     /**
1963      * @deprecated this code is not used anymore
1964      */
1965     @Deprecated
1966     protected String getDefaultSuffix(FacesContext context) throws FacesException
1967     {
1968         if (_defaultSuffix == null)
1969         {
1970             ExternalContext eContext = context.getExternalContext();
1971 
1972             String viewSuffix = eContext.getInitParameter(ViewHandler.DEFAULT_SUFFIX_PARAM_NAME);
1973 
1974             _defaultSuffix = viewSuffix == null ? ViewHandler.DEFAULT_FACELETS_SUFFIX : viewSuffix;
1975         }
1976 
1977         return _defaultSuffix;
1978     }
1979 
1980     /**
1981      * @deprecated
1982      */
1983     @Deprecated
1984     protected String getRenderedViewId(FacesContext context, String actionId)
1985     {
1986         return actionId;
1987     }
1988 
1989     /**
1990      * Generate the content type
1991      *
1992      * @param context
1993      * @param orig
1994      * @return
1995      */
1996     protected String getResponseContentType(FacesContext context, String orig)
1997     {
1998         String contentType = orig;
1999 
2000         // see if we need to override the contentType
2001         Map<Object, Object> m = context.getAttributes();
2002         if (m.containsKey("facelets.ContentType"))
2003         {
2004             contentType = (String) m.get("facelets.ContentType");
2005             if (log.isLoggable(Level.FINEST))
2006             {
2007                 log.finest("Facelet specified alternate contentType '" + contentType + "'");
2008             }
2009         }
2010 
2011         // safety check
2012         if (contentType == null)
2013         {
2014             contentType = "text/html";
2015             log.finest("ResponseWriter created had a null ContentType, defaulting to text/html");
2016         }
2017 
2018         return contentType;
2019     }
2020 
2021     /**
2022      * Generate the encoding
2023      *
2024      * @param context
2025      * @param orig
2026      * @return
2027      */
2028     protected String getResponseEncoding(FacesContext context, String orig)
2029     {
2030         String encoding = orig;
2031 
2032         // see if we need to override the encoding
2033         Map<Object, Object> m = context.getAttributes();
2034         Map<String, Object> sm = context.getExternalContext().getSessionMap();
2035 
2036         // 1. check the request attribute
2037         if (m.containsKey(PARAM_ENCODING))
2038         {
2039             encoding = (String) m.get(PARAM_ENCODING);
2040             if (encoding != null && log.isLoggable(Level.FINEST))
2041             {
2042                 log.finest("Facelet specified alternate encoding '" + encoding + "'");
2043             }
2044 
2045             sm.put(CHARACTER_ENCODING_KEY, encoding);
2046         }
2047 
2048         // 2. get it from request
2049         Object request = context.getExternalContext().getRequest();
2050         if (encoding == null)
2051         {
2052             encoding = context.getExternalContext().getRequestCharacterEncoding();
2053         }
2054 
2055         // 3. get it from the session
2056         if (encoding == null)
2057         {
2058             encoding = (String) sm.get(CHARACTER_ENCODING_KEY);
2059             if (encoding != null && log.isLoggable(Level.FINEST))
2060             {
2061                 log.finest("Session specified alternate encoding '" + encoding + "'");
2062             }
2063         }
2064 
2065         // 4. default it
2066         if (encoding == null)
2067         {
2068             encoding = DEFAULT_CHARACTER_ENCODING;
2069             if (log.isLoggable(Level.FINEST))
2070             {
2071                 log.finest("ResponseWriter created had a null CharacterEncoding, defaulting to " + encoding);
2072             }
2073         }
2074 
2075         return encoding;
2076     }
2077 
2078     protected void handleFaceletNotFound(FacesContext context, String viewId) throws FacesException, IOException
2079     {
2080         String actualId = context.getApplication().getViewHandler().getActionURL(context, viewId);
2081         context.getExternalContext().responseSendError(HttpServletResponse.SC_NOT_FOUND, actualId);
2082         context.responseComplete();
2083 
2084     }
2085 
2086     protected void handleRenderException(FacesContext context, Exception e)
2087             throws IOException, ELException, FacesException
2088     {
2089         /*
2090         UIViewRoot root = context.getViewRoot();
2091         StringBuffer sb = new StringBuffer(64);
2092         sb.append("Error Rendering View");
2093         if (root != null)
2094         {
2095             sb.append('[');
2096             sb.append(root.getViewId());
2097             sb.append(']');
2098         }
2099         
2100         log.log(Level.SEVERE, sb.toString(), e);
2101         */
2102 
2103         // rethrow the Exception to be handled by the ExceptionHandler
2104         if (e instanceof RuntimeException)
2105         {
2106             throw (RuntimeException) e;
2107         }
2108         else if (e instanceof IOException)
2109         {
2110             throw (IOException) e;
2111         }
2112         else
2113         {
2114             throw new FacesException(e.getMessage(), e);
2115         }
2116     }
2117 
2118     /**
2119      * Initialize the ViewHandler during its first request.
2120      */
2121     protected void initialize(FacesContext context)
2122     {
2123         log.finest("Initializing");
2124 
2125         Compiler compiler = createCompiler(context);
2126 
2127         _faceletFactory = createFaceletFactory(context, compiler);
2128 
2129         ExternalContext eContext = context.getExternalContext();
2130         _initializeBuffer(eContext);
2131         _initializeMode(eContext);
2132         
2133         // Create a component ids cache and store it on application map to
2134         // reduce the overhead associated with create such ids over and over.
2135         MyfacesConfig mfConfig = MyfacesConfig.getCurrentInstance(eContext);
2136         if (mfConfig.getComponentUniqueIdsCacheSize() > 0)
2137         {
2138             String[] componentIdsCached = SectionUniqueIdCounter.generateUniqueIdCache("_", 
2139                     mfConfig.getComponentUniqueIdsCacheSize());
2140             eContext.getApplicationMap().put(
2141                     CACHED_COMPONENT_IDS, componentIdsCached);
2142         }
2143 
2144         log.finest("Initialization Successful");
2145     }
2146 
2147     /**
2148      * Load the various decorators for Facelets.
2149      *
2150      * @param context
2151      *            the current FacesContext
2152      * @param compiler
2153      *            the page compiler
2154      */
2155     protected void loadDecorators(FacesContext context, Compiler compiler)
2156     {
2157         String param = WebConfigParamUtils.getStringInitParameter(context.getExternalContext(), PARAMS_DECORATORS);
2158         if (param != null)
2159         {
2160             for (String decorator : param.split(";"))
2161             {
2162                 try
2163                 {
2164                     compiler.addTagDecorator((TagDecorator) ReflectionUtil.forName(decorator).newInstance());
2165                     if (log.isLoggable(Level.FINE))
2166                     {
2167                         log.fine("Successfully loaded decorator: " + decorator);
2168                     }
2169                 }
2170                 catch (Exception e)
2171                 {
2172                     log.log(Level.SEVERE, "Error Loading decorator: " + decorator, e);
2173                 }
2174             }
2175         }
2176     }
2177 
2178     /**
2179      * Load the various tag libraries for Facelets.
2180      *
2181      * @param context
2182      *            the current FacesContext
2183      * @param compiler
2184      *            the page compiler
2185      */
2186     protected void loadLibraries(FacesContext context, Compiler compiler)
2187     {
2188         ExternalContext eContext = context.getExternalContext();
2189 
2190         compiler.addTagLibrary(new CoreLibrary());
2191         compiler.addTagLibrary(new HtmlLibrary());
2192         compiler.addTagLibrary(new UILibrary());
2193         compiler.addTagLibrary(new JstlCoreLibrary());
2194         compiler.addTagLibrary(new JstlFnLibrary());
2195         compiler.addTagLibrary(new CompositeLibrary());
2196         compiler.addTagLibrary(new CompositeResourceLibrary(context));
2197 
2198         String param = WebConfigParamUtils.getStringInitParameter(eContext, PARAMS_LIBRARIES);
2199         if (param != null)
2200         {
2201             for (String library : param.split(";"))
2202             {
2203                 try
2204                 {
2205                     URL src = eContext.getResource(library.trim());
2206                     if (src == null)
2207                     {
2208                         throw new FileNotFoundException(library);
2209                     }
2210 
2211                     TagLibrary tl = TagLibraryConfig.create(context, src);
2212                     if (tl != null)
2213                     {
2214                         compiler.addTagLibrary(tl);
2215                     }
2216                     if (log.isLoggable(Level.FINE))
2217                     {
2218                         log.fine("Successfully loaded library: " + library);
2219                     }
2220                 }
2221                 catch (IOException e)
2222                 {
2223                     log.log(Level.SEVERE, "Error Loading library: " + library, e);
2224                 }
2225             }
2226         }
2227     }
2228 
2229     /**
2230      * Load the various options for Facelets compiler. Currently only comment skipping is supported.
2231      *
2232      * @param context
2233      *            the current FacesContext
2234      * @param compiler
2235      *            the page compiler
2236      */
2237     protected void loadOptions(FacesContext context, Compiler compiler)
2238     {
2239         ExternalContext eContext = context.getExternalContext();
2240 
2241         // skip comments?
2242         compiler.setTrimmingComments(WebConfigParamUtils.getBooleanInitParameter(
2243                 eContext, PARAMS_SKIP_COMMENTS, false));
2244     }
2245 
2246     /**
2247      * {@inheritDoc}
2248      */
2249     @Override
2250     protected void sendSourceNotFound(FacesContext context, String message)
2251     {
2252         try
2253         {
2254             context.responseComplete();
2255             context.getExternalContext().responseSendError(HttpServletResponse.SC_NOT_FOUND, message);
2256         }
2257         catch (IOException ioe)
2258         {
2259             throw new FacesException(ioe);
2260         }
2261     }
2262 
2263     /**
2264      * Gets the Facelet representing the specified view identifier.
2265      *
2266      * @param viewId
2267      *            the view identifier
2268      *
2269      * @return the Facelet representing the specified view identifier
2270      *
2271      * @throws IOException
2272      *             if a read or parsing error occurs
2273      */
2274     private Facelet _getFacelet(String viewId) throws IOException
2275     {
2276         // grab our FaceletFactory and create a Facelet
2277         FaceletFactory.setInstance(_faceletFactory);
2278         try
2279         {
2280             return _faceletFactory.getFacelet(viewId);
2281         }
2282         finally
2283         {
2284             FaceletFactory.setInstance(null);
2285         }
2286     }
2287 
2288     private Facelet _getViewMetadataFacelet(String viewId) throws IOException
2289     {
2290         // grab our FaceletFactory and create a Facelet used to create view metadata
2291         FaceletFactory.setInstance(_faceletFactory);
2292         try
2293         {
2294             return _faceletFactory.getViewMetadataFacelet(viewId);
2295         }
2296         finally
2297         {
2298             FaceletFactory.setInstance(null);
2299         }
2300     }
2301 
2302 
2303     private void _initializeBuffer(ExternalContext context)
2304     {
2305         _bufferSize = WebConfigParamUtils.getIntegerInitParameter(context, PARAMS_BUFFER_SIZE, -1);
2306     }
2307 
2308     private void _initializeMode(ExternalContext context)
2309     {
2310         String facesVersion = RuntimeConfig.getCurrentInstance(context).getFacesVersion();
2311         boolean partialStateSavingDefault;
2312 
2313         // Per spec section 11.1.3, the default value for the partial state saving feature needs
2314         // to be true if 2.0, false otherwise.
2315 
2316         partialStateSavingDefault = "2.0".equals(facesVersion);
2317 
2318         // In jsf 2.0 this code evolve as PartialStateSaving feature
2319         //_buildBeforeRestore = _getBooleanParameter(context, PARAM_BUILD_BEFORE_RESTORE, false);
2320         _partialStateSaving = WebConfigParamUtils.getBooleanInitParameter(context,
2321                 StateManager.PARTIAL_STATE_SAVING_PARAM_NAME, partialStateSavingDefault);
2322 
2323         String[] viewIds = StringUtils.splitShortString(WebConfigParamUtils.getStringInitParameter(context,
2324                 StateManager.FULL_STATE_SAVING_VIEW_IDS_PARAM_NAME), ',');
2325 
2326         if (viewIds.length > 0)
2327         {
2328             _viewIds = new HashSet<String>(viewIds.length, 1.0f);
2329             Collections.addAll(_viewIds, viewIds);
2330         }
2331         else
2332         {
2333             _viewIds = null;
2334         }
2335 
2336         if (_partialStateSaving)
2337         {
2338             _refreshTransientBuildOnPSS = MyfacesConfig.getCurrentInstance(context).isRefreshTransientBuildOnPSS();
2339             _refreshTransientBuildOnPSSAuto = MyfacesConfig.getCurrentInstance(context).isRefreshTransientBuildOnPSSAuto();
2340         }
2341     }
2342 
2343     private boolean _usePartialStateSavingOnThisView(String viewId)
2344     {
2345         return _partialStateSaving && !(_viewIds != null && _viewIds.contains(viewId));
2346     }
2347 
2348     private class FaceletViewMetadata extends ViewMetadataBase
2349     {
2350         /**
2351          * Constructor
2352          *
2353          * Note that this viewId is not the one after calculateViewId() method
2354          */
2355         public FaceletViewMetadata(String viewId)
2356         {
2357             super(viewId);
2358         }
2359 
2360         /**
2361          * {@inheritDoc}
2362          */
2363         @Override
2364         public UIViewRoot createMetadataView(FacesContext context)
2365         {
2366             try
2367             {
2368                 context.setProcessingEvents(false);
2369 
2370                 // spec doesn't say that this is necessary, but we blow up later if
2371                 // the viewroot isn't available from the FacesContext.
2372                 // -= Leonardo Uribe =- since it is supposed when we apply view metadata
2373                 // facelet we don't apply components with renderers and we don't call getRenderKit()
2374                 // it is safe to let this one commented
2375                 // context.setViewRoot(view);
2376 
2377                 // -= Leonardo Uribe =- This part is related to section 2.5.5 of jsf 2.0 spec.
2378                 // In theory what we need here is fill UIViewRoot.METADATA_FACET_NAME facet
2379                 // with UIViewParameter instances. Later, ViewHandlerImpl.getBookmarkableURL(),
2380                 // ViewHandlerImpl.getRedirectURL() and UIViewRoot.encodeEnd uses them. 
2381                 // For now, the only way to do this is call buildView(context,view) method, but 
2382                 // this is a waste of resources. We need to find another way to handle facelets view metadata.
2383                 // Call to buildView causes the view is not created on Render Response phase,
2384                 // if buildView is called from here all components pass through current lifecycle and only
2385                 // UIViewParameter instances should be taken into account.
2386                 // It should be an additional call to buildView on Render Response phase.
2387                 // buildView(context, view);
2388 
2389                 context.getAttributes().put(BUILDING_VIEW_METADATA, Boolean.TRUE);
2390 
2391                 // we have to invoke createView() on the application's ViewHandler
2392                 // here instead of invoking it directly in FaceletVDL, because
2393                 // the ViewHandler might be wrapped and wants to do some work
2394                 // in createView() (e.g. in Trinidad - see MYFACES-2641)
2395                 UIViewRoot view = context.getApplication().getViewHandler().createView(context, getViewId());
2396 
2397                 if (view != null)
2398                 {
2399                     // inside createView(context,viewId), calculateViewId() is called and
2400                     // the result is stored inside created UIViewRoot, so we can safely take the derived
2401                     // viewId from there.
2402                     Facelet facelet = _getViewMetadataFacelet(view.getViewId());
2403                     facelet.apply(context, view);
2404                 }
2405 
2406                 return view;
2407             }
2408             catch (IOException ioe)
2409             {
2410                 throw new FacesException(ioe);
2411             }
2412             finally
2413             {
2414                 context.getAttributes().remove(BUILDING_VIEW_METADATA);
2415 
2416                 context.setProcessingEvents(true);
2417             }
2418         }
2419     }
2420 
2421 }