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