Coverage Report - org.apache.myfaces.view.facelets.tag.jsf.ComponentTagHandlerDelegate
 
Classes in this File Line Coverage Branch Coverage Complexity
ComponentTagHandlerDelegate
0%
0/267
0%
0/228
11.25
ComponentTagHandlerDelegate$1
N/A
N/A
11.25
ComponentTagHandlerDelegate$PublishFaceletDynamicComponentRefreshTransientBuildCallback
0%
0/3
N/A
11.25
 
 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.tag.jsf;
 20  
 
 21  
 import java.io.IOException;
 22  
 import java.util.Collections;
 23  
 import java.util.EnumSet;
 24  
 import java.util.Iterator;
 25  
 import java.util.List;
 26  
 import java.util.Map;
 27  
 import java.util.Set;
 28  
 import java.util.logging.Level;
 29  
 import java.util.logging.Logger;
 30  
 
 31  
 import javax.el.ValueExpression;
 32  
 import javax.faces.FacesWrapper;
 33  
 import javax.faces.application.Application;
 34  
 import javax.faces.application.ProjectStage;
 35  
 import javax.faces.component.ActionSource;
 36  
 import javax.faces.component.EditableValueHolder;
 37  
 import javax.faces.component.UIComponent;
 38  
 import javax.faces.component.UIOutput;
 39  
 import javax.faces.component.UniqueIdVendor;
 40  
 import javax.faces.component.ValueHolder;
 41  
 import javax.faces.component.behavior.ClientBehaviorHolder;
 42  
 import javax.faces.component.visit.VisitCallback;
 43  
 import javax.faces.component.visit.VisitContext;
 44  
 import javax.faces.component.visit.VisitHint;
 45  
 import javax.faces.component.visit.VisitResult;
 46  
 import javax.faces.context.FacesContext;
 47  
 import javax.faces.event.PhaseId;
 48  
 import javax.faces.validator.BeanValidator;
 49  
 import javax.faces.validator.Validator;
 50  
 import javax.faces.view.EditableValueHolderAttachedObjectHandler;
 51  
 import javax.faces.view.facelets.ComponentConfig;
 52  
 import javax.faces.view.facelets.ComponentHandler;
 53  
 import javax.faces.view.facelets.FaceletContext;
 54  
 import javax.faces.view.facelets.MetaRuleset;
 55  
 import javax.faces.view.facelets.TagAttribute;
 56  
 import javax.faces.view.facelets.TagException;
 57  
 import javax.faces.view.facelets.TagHandlerDelegate;
 58  
 import javax.faces.view.facelets.ValidatorHandler;
 59  
 
 60  
 import org.apache.myfaces.util.ExternalSpecifications;
 61  
 import org.apache.myfaces.view.facelets.AbstractFaceletContext;
 62  
 import org.apache.myfaces.view.facelets.ComponentState;
 63  
 import org.apache.myfaces.view.facelets.DefaultFaceletsStateManagementStrategy;
 64  
 import org.apache.myfaces.view.facelets.FaceletCompositionContext;
 65  
 import org.apache.myfaces.view.facelets.FaceletDynamicComponentRefreshTransientBuildEvent;
 66  
 import org.apache.myfaces.view.facelets.FaceletViewDeclarationLanguage;
 67  
 import org.apache.myfaces.view.facelets.FaceletViewDeclarationLanguageBase;
 68  
 import org.apache.myfaces.view.facelets.el.CompositeComponentELUtils;
 69  
 import org.apache.myfaces.view.facelets.tag.MetaRulesetImpl;
 70  
 import org.apache.myfaces.view.facelets.tag.jsf.core.AjaxHandler;
 71  
 import org.apache.myfaces.view.facelets.tag.jsf.core.FacetHandler;
 72  
 
 73  
 /**
 74  
  *  
 75  
  * Implementation of the tag logic used in the JSF specification. 
 76  
  * 
 77  
  * @see org.apache.myfaces.view.facelets.tag.jsf.ComponentHandler
 78  
  * @author Leonardo Uribe (latest modification by $Author$)
 79  
  * @version $Revision$ $Date$
 80  
  *
 81  
  * @since 2.0
 82  
  */
 83  
 public class ComponentTagHandlerDelegate extends TagHandlerDelegate
 84  
 {
 85  0
     private final static Logger log = Logger.getLogger(ComponentTagHandlerDelegate.class.getName());
 86  
     
 87  0
     private static final Set<VisitHint> VISIT_HINTS_DYN_REFRESH = Collections.unmodifiableSet( 
 88  
             EnumSet.of(VisitHint.SKIP_ITERATION));
 89  
 
 90  
     private final ComponentHandler _delegate;
 91  
 
 92  
     private final String _componentType;
 93  
 
 94  
     private final TagAttribute _id;
 95  
 
 96  
     private final String _rendererType;
 97  
     
 98  
     private final ComponentBuilderHandler _componentBuilderHandlerDelegate;
 99  
     
 100  
     private final RelocatableResourceHandler _relocatableResourceHandler;
 101  
 
 102  
     @SuppressWarnings("unchecked")
 103  
     public ComponentTagHandlerDelegate(ComponentHandler delegate)
 104  0
     {
 105  0
         _delegate = delegate;
 106  0
         ComponentConfig delegateComponentConfig = delegate.getComponentConfig();
 107  0
         _componentType = delegateComponentConfig.getComponentType();
 108  0
         _rendererType = delegateComponentConfig.getRendererType();
 109  0
         _id = delegate.getTagAttribute("id");      
 110  
         
 111  0
         ComponentHandler handler = _delegate;
 112  0
         boolean found = false;
 113  0
         while(handler != null && !found)
 114  
         {
 115  0
             if (handler instanceof ComponentBuilderHandler)
 116  
             {
 117  0
                 found = true;
 118  
             }
 119  0
             else if (handler instanceof FacesWrapper)
 120  
             {
 121  0
                 handler = ((FacesWrapper<? extends ComponentHandler>)handler).getWrapped();
 122  
             }
 123  
             else
 124  
             {
 125  0
                 handler = null;
 126  
             }
 127  
         }
 128  0
         if (found)
 129  
         {
 130  0
             _componentBuilderHandlerDelegate = (ComponentBuilderHandler) handler;
 131  
         }
 132  
         else
 133  
         {
 134  0
             _componentBuilderHandlerDelegate = null;
 135  
         }
 136  
         
 137  
         //Check if this component is instance of RelocatableResourceHandler
 138  0
         handler = _delegate;
 139  0
         found = false;
 140  0
         while(handler != null && !found)
 141  
         {
 142  0
             if (handler instanceof RelocatableResourceHandler)
 143  
             {
 144  0
                 found = true;
 145  
             }
 146  0
             else if (handler instanceof FacesWrapper)
 147  
             {
 148  0
                 handler = ((FacesWrapper<? extends ComponentHandler>)handler).getWrapped();
 149  
             }
 150  
             else
 151  
             {
 152  0
                 handler = null;
 153  
             }
 154  
         }
 155  0
         if (found)
 156  
         {
 157  0
             _relocatableResourceHandler = (RelocatableResourceHandler) handler;
 158  
         }
 159  
         else
 160  
         {
 161  
             // Check if the component is a relocatable component done overriding the tag handler
 162  0
             if (_componentType != null && _rendererType != null &&
 163  
                 (_rendererType.equals("javax.faces.resource.Script") ||
 164  
                  _rendererType.equals("javax.faces.resource.Stylesheet")) &&
 165  
                 _componentType.equals(UIOutput.COMPONENT_TYPE))
 166  
             {
 167  0
                 _relocatableResourceHandler = ComponentRelocatableResourceHandler.INSTANCE;
 168  
             }   
 169  
             else
 170  
             {
 171  0
                 _relocatableResourceHandler = null;
 172  
             }
 173  
         }
 174  0
     }
 175  
 
 176  
     /**
 177  
      * Method handles UIComponent tree creation in accordance with the JSF 1.2 spec.
 178  
      * <ol>
 179  
      * <li>First determines this UIComponent's id by calling {@link #getId(FaceletContext) getId(FaceletContext)}.</li>
 180  
      * <li>Search the parent for an existing UIComponent of the id we just grabbed</li>
 181  
      * <li>If found, {@link FaceletCompositionContext#markForDeletion(UIComponent) mark} its children for deletion.</li>
 182  
      * <li>If <i>not</i> found, call {@link #createComponent(FaceletContext) createComponent}.
 183  
      * <ol>
 184  
      * <li>Only here do we apply
 185  
      * {@link javax.faces.view.facelets.TagHandler#setAttributes(FaceletCompositionContext, Object) attributes}</li>
 186  
      * <li>Set the UIComponent's id</li>
 187  
      * <li>Set the RendererType of this instance</li>
 188  
      * </ol>
 189  
      * </li>
 190  
      * <li>Now apply the nextHandler, passing the UIComponent we've created/found.</li>
 191  
      * <li>Now add the UIComponent to the passed parent</li>
 192  
      * <li>Lastly, if the UIComponent already existed (found),
 193  
      * then {@link #finalizeForDeletion(FaceletCompositionContext, UIComponent) finalize}
 194  
      * for deletion.</li>
 195  
      * </ol>
 196  
      * 
 197  
      * @see javax.faces.view.facelets.FaceletHandler#apply(javax.faces.view.facelets.FaceletContext,
 198  
      * javax.faces.component.UIComponent)
 199  
      * 
 200  
      * @throws TagException
 201  
      *             if the UIComponent parent is null
 202  
      */
 203  
     @SuppressWarnings("unchecked")
 204  
     @Override
 205  
     public void apply(FaceletContext ctx, UIComponent parent) throws IOException
 206  
     {
 207  
         // make sure our parent is not null
 208  0
         if (parent == null)
 209  
         {
 210  0
             throw new TagException(_delegate.getTag(), "Parent UIComponent was null");
 211  
         }
 212  
         
 213  0
         FacesContext facesContext = ctx.getFacesContext();
 214  
 
 215  
         // possible facet scoped
 216  0
         String facetName = this.getFacetName(ctx, parent);
 217  
 
 218  
         // our id
 219  0
         String id = ctx.generateUniqueId(_delegate.getTagId());
 220  
 
 221  
         // Cast to use UniqueIdVendor stuff
 222  0
         FaceletCompositionContext mctx = (FaceletCompositionContext) FaceletCompositionContext.getCurrentInstance(ctx);
 223  
                 
 224  
         // grab our component
 225  0
         UIComponent c = null;
 226  
         //boolean componentFoundInserted = false;
 227  
 
 228  
         //Used to preserve the original parent. Note when the view is being refreshed, the real parent could be
 229  
         //another component.
 230  0
         UIComponent oldParent = parent;
 231  
         
 232  0
         if (mctx.isRefreshingSection())
 233  
         {
 234  0
             if (_relocatableResourceHandler != null)
 235  
             {
 236  0
                 c = _relocatableResourceHandler.findChildByTagId(ctx, parent, id);
 237  
             }
 238  
             else
 239  
             {
 240  0
                 if (facetName != null)
 241  
                 {
 242  0
                     c = ComponentSupport.findChildInFacetByTagId(parent, id, facetName);
 243  
                 }
 244  
                 else
 245  
                 {
 246  0
                     c = ComponentSupport.findChildInChildrenByTagId(parent, id);
 247  
                 }
 248  
             }
 249  
         }
 250  0
         boolean componentFound = false;
 251  0
         if (c != null)
 252  
         {
 253  0
             componentFound = true;
 254  
             // Check if the binding needs dynamic refresh and if that so, invoke the refresh from this location, to
 255  
             // preserve the same context
 256  0
             if (_delegate.getBinding() != null &&
 257  
                 c.getAttributes().containsKey(
 258  
                     FaceletDynamicComponentRefreshTransientBuildEvent.DYNAMIC_COMPONENT_BINDING_NEEDS_REFRESH))
 259  
             {
 260  0
                 VisitContext visitContext = (VisitContext) mctx.getVisitContextFactory().
 261  
                     getVisitContext(facesContext, null, VISIT_HINTS_DYN_REFRESH);
 262  0
                 c.visitTree(visitContext, new PublishFaceletDynamicComponentRefreshTransientBuildCallback());
 263  
             }
 264  
             
 265  0
             mctx.incrementUniqueComponentId();
 266  
             
 267  
             // mark all children for cleaning
 268  0
             if (log.isLoggable(Level.FINE))
 269  
             {
 270  0
                 log.fine(_delegate.getTag() + " Component[" + id + "] Found, marking children for cleanup");
 271  
             }
 272  
 
 273  
             // The call for mctx.markForDeletion(c) is always necessary, because
 274  
             // component resource relocation occur as an effect of PostAddToViewEvent,
 275  
             // so at this point it is unknown if the component was relocated or not.
 276  0
             mctx.markForDeletion(c);
 277  
 
 278  0
             if (_relocatableResourceHandler != null)
 279  
             {
 280  0
                 mctx.markRelocatableResourceForDeletion(c);
 281  
             }
 282  
         }
 283  
         else
 284  
         {
 285  0
             c = this.createComponent(ctx);
 286  0
             if (log.isLoggable(Level.FINE))
 287  
             {
 288  0
                 log.fine(_delegate.getTag() + " Component[" + id + "] Created: " + c.getClass().getName());
 289  
             }
 290  
             
 291  0
             _delegate.setAttributes(ctx, c);
 292  
 
 293  
             // mark it owned by a facelet instance
 294  0
             c.getAttributes().put(ComponentSupport.MARK_CREATED, id);
 295  
 
 296  0
             if (facesContext.isProjectStage(ProjectStage.Development))
 297  
             {
 298  0
                 c.getAttributes().put(UIComponent.VIEW_LOCATION_KEY,
 299  
                         _delegate.getTag().getLocation());
 300  
             }
 301  
 
 302  
             // assign our unique id
 303  0
             if (this._id != null)
 304  
             {
 305  0
                 mctx.incrementUniqueComponentId();
 306  0
                 c.setId(this._id.getValue(ctx));
 307  
             }
 308  
             else
 309  
             {
 310  0
                 String componentId = mctx.generateUniqueComponentId();
 311  0
                 UniqueIdVendor uniqueIdVendor = mctx.getUniqueIdVendorFromStack();
 312  0
                 if (uniqueIdVendor == null)
 313  
                 {
 314  0
                     uniqueIdVendor = facesContext.getViewRoot();
 315  
                     
 316  0
                     if (uniqueIdVendor == null)
 317  
                     {
 318  
                         // facesContext.getViewRoot() returns null here if we are in
 319  
                         // phase restore view, so we have to try to get the view root
 320  
                         // via the method in ComponentSupport and our parent
 321  0
                         uniqueIdVendor = ComponentSupport.getViewRoot(ctx, parent);
 322  
                     }
 323  
                 }
 324  0
                 if (uniqueIdVendor != null)
 325  
                 {
 326  
                     // UIViewRoot implements UniqueIdVendor, so there is no need to cast to UIViewRoot
 327  
                     // and call createUniqueId()
 328  0
                     String uid = uniqueIdVendor.createUniqueId(facesContext, componentId);
 329  0
                     c.setId(uid);
 330  
                 }
 331  
             }
 332  
 
 333  0
             if (this._rendererType != null)
 334  
             {
 335  0
                 c.setRendererType(this._rendererType);
 336  
             }
 337  
 
 338  
             // hook method
 339  0
             _delegate.onComponentCreated(ctx, c, parent);
 340  
             
 341  0
             if (_relocatableResourceHandler != null && 
 342  
                 _relocatableResourceHandler instanceof ComponentRelocatableResourceHandler)
 343  
             {
 344  0
                 UIComponent parentCompositeComponent
 345  
                         = mctx.getCompositeComponentFromStack();
 346  0
                 if (parentCompositeComponent != null)
 347  
                 {
 348  0
                     c.getAttributes().put(CompositeComponentELUtils.LOCATION_KEY,
 349  
                             parentCompositeComponent.getAttributes().get(CompositeComponentELUtils.LOCATION_KEY));
 350  
                 }
 351  
             }
 352  
             
 353  0
             if (mctx.isRefreshingTransientBuild() && _relocatableResourceHandler != null)
 354  
             {
 355  0
                 mctx.markRelocatableResourceForDeletion(c);
 356  
             }
 357  
         }
 358  0
         c.pushComponentToEL(facesContext, c);
 359  
 
 360  0
         if (c instanceof UniqueIdVendor)
 361  
         {
 362  0
             mctx.pushUniqueIdVendorToStack((UniqueIdVendor)c);
 363  
         }
 364  
         
 365  0
         if (mctx.isDynamicComponentTopLevel())
 366  
         {
 367  0
             mctx.setDynamicComponentTopLevel(false);
 368  0
             _delegate.applyNextHandler(ctx, c);
 369  0
             mctx.setDynamicComponentTopLevel(true);
 370  
         }
 371  
         else
 372  
         {
 373  
             // first allow c to get populated
 374  0
             _delegate.applyNextHandler(ctx, c);
 375  
         }
 376  
         
 377  0
         boolean oldProcessingEvents = facesContext.isProcessingEvents();
 378  
         // finish cleaning up orphaned children
 379  0
         if (componentFound && !mctx.isDynamicComponentTopLevel())
 380  
         {
 381  0
             mctx.finalizeForDeletion(c);
 382  
 
 383  
             //if (!componentFoundInserted)
 384  
             //{
 385  0
                 if (mctx.isRefreshingSection())
 386  
                 {
 387  0
                     facesContext.setProcessingEvents(false);
 388  0
                     if (_relocatableResourceHandler != null &&
 389  
                         parent != null && !parent.equals(c.getParent()))
 390  
                     {
 391  
                         // Replace parent with the relocated parent.
 392  0
                         parent = c.getParent();
 393  
                         // Since we changed the parent, the facetName becomes invalid, because it points
 394  
                         // to the component before relocation. We need to find the right facetName (if any) so we can
 395  
                         // refresh the component properly.
 396  0
                         UIComponent c1 = ComponentSupport.findChildInChildrenByTagId(parent, id);
 397  0
                         if (c1 == null)
 398  
                         {
 399  0
                             facetName = ComponentSupport.findChildInFacetsByTagId(parent, id);
 400  
                         }
 401  
                         else
 402  
                         {
 403  0
                             facetName = null;
 404  
                         }
 405  
                     }
 406  0
                     ComponentSupport.setCachedFacesContext(c, facesContext);
 407  
                 }
 408  0
                 if (facetName == null)
 409  
                 {
 410  0
                     parent.getChildren().remove(c);
 411  
                 }
 412  
                 else
 413  
                 {
 414  0
                     ComponentSupport.removeFacet(ctx, parent, c, facetName);
 415  
                 }
 416  0
                 if (mctx.isRefreshingSection())
 417  
                 {
 418  0
                     ComponentSupport.setCachedFacesContext(c, null);
 419  0
                     facesContext.setProcessingEvents(oldProcessingEvents);
 420  
                 }
 421  
             //}
 422  
         }
 423  
 
 424  
 
 425  0
         if (!componentFound)
 426  
         {
 427  0
             if (c instanceof ClientBehaviorHolder && !UIComponent.isCompositeComponent(c))
 428  
             {
 429  0
                 Iterator<AjaxHandler> it = ((AbstractFaceletContext) ctx).getAjaxHandlers();
 430  0
                 if (it != null)
 431  
                 {
 432  0
                     while(it.hasNext())
 433  
                     {
 434  0
                         it.next().applyAttachedObject(facesContext, c);
 435  
                     }
 436  
                 }
 437  
             }
 438  
             
 439  0
             if (c instanceof EditableValueHolder)
 440  
             {
 441  
                 // add default validators here, because this feature 
 442  
                 // is only available in facelets (see MYFACES-2362 for details)
 443  0
                 addEnclosingAndDefaultValidators(ctx, mctx, facesContext, (EditableValueHolder) c);
 444  
             }
 445  
         }
 446  
         
 447  0
         _delegate.onComponentPopulated(ctx, c, oldParent);
 448  
 
 449  0
         if (!mctx.isDynamicComponentTopLevel() || !componentFound)
 450  
         {
 451  0
             if (componentFound && mctx.isRefreshingSection())
 452  
             {
 453  0
                 facesContext.setProcessingEvents(false);
 454  0
                 ComponentSupport.setCachedFacesContext(c, facesContext);
 455  
             }
 456  0
             if (facetName == null)
 457  
             {
 458  0
                 parent.getChildren().add(c);
 459  
             }
 460  
             else
 461  
             {
 462  0
                 ComponentSupport.addFacet(ctx, parent, c, facetName);
 463  
             }
 464  0
             if (componentFound && mctx.isRefreshingSection())
 465  
             {
 466  0
                 ComponentSupport.setCachedFacesContext(c, null);
 467  0
                 facesContext.setProcessingEvents(oldProcessingEvents);
 468  
             }
 469  
         }
 470  
 
 471  0
         if (c instanceof UniqueIdVendor)
 472  
         {
 473  0
             mctx.popUniqueIdVendorToStack();
 474  
         }
 475  
 
 476  0
         c.popComponentFromEL(facesContext);
 477  
         
 478  0
         if (mctx.isMarkInitialState())
 479  
         {
 480  
             //Call it only if we are using partial state saving
 481  0
             c.markInitialState();
 482  
         }
 483  0
     }
 484  
     
 485  
     /**
 486  
      * Return the Facet name we are scoped in, otherwise null
 487  
      * 
 488  
      * @param ctx
 489  
      * @return
 490  
      */
 491  
     protected final String getFacetName(FaceletContext ctx, UIComponent parent)
 492  
     {
 493  0
         return (String) parent.getAttributes().get(FacetHandler.KEY);
 494  
     }
 495  
 
 496  
     /**
 497  
      * If the binding attribute was specified, use that in conjuction with our componentType String variable to call
 498  
      * createComponent on the Application, otherwise just pass the componentType String. <p /> If the binding was used,
 499  
      * then set the ValueExpression "binding" on the created UIComponent.
 500  
      * 
 501  
      * @see Application#createComponent(javax.faces.el.ValueBinding, javax.faces.context.FacesContext, java.lang.String)
 502  
      * @see Application#createComponent(java.lang.String)
 503  
      * @param ctx
 504  
      *            FaceletContext to use in creating a component
 505  
      * @return
 506  
      */
 507  
     protected UIComponent createComponent(FaceletContext ctx)
 508  
     {
 509  0
         if (_componentBuilderHandlerDelegate != null)
 510  
         {
 511  
             // the call to Application.createComponent(FacesContext, Resource)
 512  
             // is delegated because we don't have here the required Resource instance
 513  0
             return _componentBuilderHandlerDelegate.createComponent(ctx);
 514  
         }
 515  0
         UIComponent c = null;
 516  0
         FacesContext faces = ctx.getFacesContext();
 517  0
         Application app = faces.getApplication();
 518  0
         if (_delegate.getBinding() != null)
 519  
         {
 520  0
             ValueExpression ve = _delegate.getBinding().getValueExpression(ctx, Object.class);
 521  0
             if (PhaseId.RESTORE_VIEW.equals(faces.getCurrentPhaseId()))
 522  
             {
 523  0
                 if (!ve.isReadOnly(faces.getELContext()))
 524  
                 {
 525  
                     try
 526  
                     {
 527  
                         // force reset it is an easy and cheap way to allow "binding" attribute to work on 
 528  
                         // view scope beans or flow scope beans (using a transient variable)
 529  0
                         ve.setValue(faces.getELContext(), null);
 530  
                     }
 531  0
                     catch (Exception e)
 532  
                     {
 533  
                         // ignore
 534  0
                     }
 535  
                 }
 536  
             }
 537  0
             if (this._rendererType == null)
 538  
             {
 539  0
                 c = app.createComponent(ve, faces, this._componentType);
 540  
             }
 541  
             else
 542  
             {
 543  0
                 c = app.createComponent(ve, faces, this._componentType, this._rendererType);
 544  
             }
 545  0
             if (c != null)
 546  
             {
 547  0
                 c.setValueExpression("binding", ve);
 548  
                 
 549  0
                 if (!ve.isReadOnly(faces.getELContext()))
 550  
                 {
 551  0
                     ComponentSupport.getViewRoot(ctx, c).getAttributes().put("oam.CALL_PRE_DISPOSE_VIEW", Boolean.TRUE);
 552  0
                     c.subscribeToEvent(PreDisposeViewEvent.class, new ClearBindingValueExpressionListener());
 553  
                 }
 554  
                 
 555  0
                 if (c.getChildCount() > 0 || c.getFacetCount() > 0)
 556  
                 {
 557  
                     // In this case, this component is used to hold a subtree that is generated
 558  
                     // dynamically. In this case, the best is mark this component to be restored
 559  
                     // fully, because this ensures the state is correctly preserved. Note this
 560  
                     // is only necessary when the component has additional children or facets,
 561  
                     // because those components requires an unique id provided by createUniqueId(),
 562  
                     // and this ensures stability of the generated ids.
 563  0
                     c.getAttributes().put(DefaultFaceletsStateManagementStrategy.COMPONENT_ADDED_AFTER_BUILD_VIEW,
 564  
                                           ComponentState.REMOVE_ADD);
 565  
                     
 566  0
                     if (FaceletViewDeclarationLanguageBase.isDynamicComponentNeedsRefresh(ctx.getFacesContext()))
 567  
                     {
 568  0
                         FaceletViewDeclarationLanguageBase.resetDynamicComponentNeedsRefreshFlag(
 569  
                                 ctx.getFacesContext());
 570  0
                         FaceletCompositionContext mctx = FaceletCompositionContext.getCurrentInstance(ctx);
 571  0
                         if (mctx.isUsingPSSOnThisView())
 572  
                         {
 573  0
                             FaceletViewDeclarationLanguage.cleanTransientBuildOnRestore(faces);
 574  
                         }
 575  
                         else
 576  
                         {
 577  0
                             FaceletViewDeclarationLanguageBase.activateDynamicComponentRefreshTransientBuild(faces);
 578  
                         }
 579  
                         //
 580  
                         // Mark top binding component to be dynamically refreshed. In that way, facelets algorithm
 581  
                         // will be able to decide if the component children requires to be refreshed dynamically 
 582  
                         // or not.
 583  0
                         c.getAttributes().put(
 584  
                                 FaceletDynamicComponentRefreshTransientBuildEvent.
 585  
                                     DYNAMIC_COMPONENT_BINDING_NEEDS_REFRESH,
 586  
                                 Boolean.TRUE);
 587  
                     }
 588  
                 }
 589  
             }
 590  0
         }
 591  
         else
 592  
         {
 593  
             // According to the, spec call the second alternative with null rendererType gives
 594  
             // the same result, but without the unnecesary call for FacesContext.getCurrentInstance().
 595  
             // Saves 1 call per component without rendererType (f:viewParam, h:column, f:selectItem, ...)
 596  
             // and it does not have any side effects (the spec javadoc mentions in a explicit way
 597  
             // that rendererType can be null!).
 598  
             /*
 599  
             if (this._rendererType == null)
 600  
             {
 601  
                 c = app.createComponent(this._componentType);
 602  
             }
 603  
             else
 604  
             {*/
 605  0
                 c = app.createComponent(faces, this._componentType, this._rendererType);
 606  
             //}
 607  
         }
 608  0
         return c;
 609  
     }
 610  
 
 611  
     /**
 612  
      * If the id TagAttribute was specified, get it's value, otherwise generate a unique id from our tagId.
 613  
      * 
 614  
      * @see TagAttribute#getValue(FaceletContext)
 615  
      * @param ctx
 616  
      *            FaceletContext to use
 617  
      * @return what should be a unique Id
 618  
      */
 619  
     protected String getId(FaceletContext ctx)
 620  
     {
 621  0
         if (this._id != null)
 622  
         {
 623  0
             return this._id.getValue(ctx);
 624  
         }
 625  0
         return ctx.generateUniqueId(_delegate.getTagId());
 626  
     }
 627  
 
 628  
     @Override
 629  
     public MetaRuleset createMetaRuleset(Class type)
 630  
     {
 631  0
         MetaRuleset m = new MetaRulesetImpl(_delegate.getTag(), type);
 632  
         // ignore standard component attributes
 633  0
         m.ignore("binding").ignore("id");
 634  
 
 635  
         // add auto wiring for attributes
 636  0
         m.addRule(ComponentRule.INSTANCE);
 637  
         
 638  
         // add special rule for passthrough attributes
 639  0
         m.addRule(PassthroughRuleImpl.INSTANCE);
 640  
 
 641  
         // if it's an ActionSource
 642  0
         if (ActionSource.class.isAssignableFrom(type))
 643  
         {
 644  0
             m.addRule(ActionSourceRule.INSTANCE);
 645  
         }
 646  
 
 647  
         // if it's a ValueHolder
 648  0
         if (ValueHolder.class.isAssignableFrom(type))
 649  
         {
 650  0
             m.addRule(ValueHolderRule.INSTANCE);
 651  
 
 652  
             // if it's an EditableValueHolder
 653  0
             if (EditableValueHolder.class.isAssignableFrom(type))
 654  
             {
 655  0
                 m.ignore("submittedValue");
 656  0
                 m.ignore("valid");
 657  0
                 m.addRule(EditableValueHolderRule.INSTANCE);
 658  
             }
 659  
         }
 660  
         
 661  
         // allow components to specify the "class" attribute 
 662  0
         m.alias("class", "styleClass");
 663  
         
 664  0
         return m;
 665  
     }
 666  
     
 667  
     /**
 668  
      * Add the default Validators to the component.
 669  
      * Also adds all validators specified by enclosing <f:validateBean> tags
 670  
      * (e.g. the BeanValidator if it is not a default validator).
 671  
      *
 672  
      * @param context The FacesContext.
 673  
      * @param mctx the AbstractFaceletContext
 674  
      * @param component The EditableValueHolder to which the validators should be added
 675  
      */
 676  
     private void addEnclosingAndDefaultValidators(FaceletContext ctx, 
 677  
                                       FaceletCompositionContext mctx, 
 678  
                                       FacesContext context, 
 679  
                                       EditableValueHolder component)
 680  
     {
 681  
         // add all enclosing validators, because they have precedence over default validators.
 682  0
         Iterator<Map.Entry<String, EditableValueHolderAttachedObjectHandler>> enclosingValidatorIds =
 683  
             mctx.getEnclosingValidatorIdsAndHandlers();
 684  0
         if (enclosingValidatorIds != null)
 685  
         {
 686  0
             while (enclosingValidatorIds.hasNext())
 687  
             {
 688  0
                 Map.Entry<String, EditableValueHolderAttachedObjectHandler> entry = enclosingValidatorIds.next();
 689  0
                 addEnclosingValidator(context, component, entry.getKey(), entry.getValue());
 690  0
             }
 691  
         }
 692  
         // add all defaultValidators
 693  0
         Map<String, String> defaultValidators = context.getApplication().getDefaultValidatorInfo();
 694  0
         if (defaultValidators != null && defaultValidators.size() != 0)
 695  
         {
 696  0
             for (Map.Entry<String, String> entry : defaultValidators.entrySet())
 697  
             {
 698  0
                 if (!mctx.containsEnclosingValidatorId(entry.getKey()))
 699  
                 {
 700  0
                     addDefaultValidator(ctx, mctx, context, component, entry.getKey(), entry.getValue());
 701  
                 }
 702  0
             }
 703  
         }
 704  0
     }
 705  
 
 706  
     private void addDefaultValidator(FaceletContext ctx, FaceletCompositionContext mctx, FacesContext context, 
 707  
             EditableValueHolder component, String validatorId, String validatorClassName)
 708  
     {
 709  0
         Validator enclosingValidator = null;
 710  
         
 711  0
         if (validatorClassName == null)
 712  
         {
 713  
             // we have no class name for validators of enclosing <f:validateBean> tags
 714  
             // --> we have to create it to get the class name
 715  
             // note that normally we can use this instance later anyway!
 716  0
             enclosingValidator = context.getApplication().createValidator(validatorId);
 717  0
             validatorClassName = enclosingValidator.getClass().getName();
 718  
         }
 719  
         
 720  
         // check if the validator is already registered for the given component
 721  
         // this happens if <f:validateBean /> is nested inside the component on the view
 722  0
         Validator validator = null;
 723  0
         for (Validator v : component.getValidators())
 724  
         {
 725  0
             if (v.getClass().getName().equals(validatorClassName))
 726  
             {
 727  
                 // found
 728  0
                 validator = v;
 729  0
                 break;
 730  
             }
 731  
         }
 732  
         
 733  0
         if (validator == null)
 734  
         {
 735  0
             if (shouldAddDefaultValidator(ctx, mctx, component, validatorId))
 736  
             {
 737  0
                 if (enclosingValidator != null)
 738  
                 {
 739  
                     // we can use the instance from before
 740  0
                     validator = enclosingValidator;
 741  
                 }
 742  
                 else
 743  
                 {
 744  
                     // create it
 745  0
                     validator = context.getApplication().createValidator(validatorId);
 746  
                 }
 747  
                 // add the validator to the component
 748  0
                 component.addValidator(validator);
 749  
             }
 750  
             else
 751  
             {
 752  
                 // we should not add the validator
 753  0
                 return;
 754  
             }
 755  
         }
 756  
         
 757  
         // special things to configure for a BeanValidator
 758  0
         if (validator instanceof BeanValidator)
 759  
         {
 760  0
             BeanValidator beanValidator = (BeanValidator) validator;
 761  
             
 762  
             // check the validationGroups
 763  0
             String validationGroups =  beanValidator.getValidationGroups();
 764  0
             if (validationGroups == null 
 765  
                     || validationGroups.matches(BeanValidator.EMPTY_VALIDATION_GROUPS_PATTERN))
 766  
             {
 767  
                 // no validationGroups available
 768  
                 // --> get the validationGroups from the stack
 769  
                 //String stackGroup = mctx.getFirstValidationGroupFromStack();
 770  
                 //if (stackGroup != null)
 771  
                 //{
 772  
                 //    validationGroups = stackGroup;
 773  
                 //}
 774  
                 //else
 775  
                 //{
 776  
                     // no validationGroups on the stack
 777  
                     // --> set the default validationGroup
 778  0
                     validationGroups = javax.validation.groups.Default.class.getName();
 779  
                 //}
 780  0
                 beanValidator.setValidationGroups(validationGroups);
 781  
             }
 782  
         }
 783  0
     }
 784  
 
 785  
     /**
 786  
      * Determine if the validator with the given validatorId should be added.
 787  
      *
 788  
      * @param validatorId The validatorId.
 789  
      * @param facesContext The FacesContext.
 790  
      * @param mctx the AbstractFaceletContext
 791  
      * @param component The EditableValueHolder to which the validator should be added.
 792  
      * @return true if the Validator should be added, false otherwise.
 793  
      */
 794  
     @SuppressWarnings("unchecked")
 795  
     private boolean shouldAddDefaultValidator(FaceletContext ctx, FaceletCompositionContext mctx,
 796  
                                               EditableValueHolder component, 
 797  
                                               String validatorId)
 798  
     {
 799  
         // check if the validatorId is on the exclusion list on the component
 800  0
         List<String> exclusionList 
 801  
                 = (List<String>) ((UIComponent) component).getAttributes()
 802  
                         .get(ValidatorTagHandlerDelegate.VALIDATOR_ID_EXCLUSION_LIST_KEY);
 803  0
         if (exclusionList != null)
 804  
         {
 805  0
             for (String excludedId : exclusionList)
 806  
             {
 807  0
                 if (excludedId.equals(validatorId))
 808  
                 {
 809  0
                     return false;
 810  
                 }
 811  0
             }
 812  
         }
 813  
         
 814  
         // check if the validatorId is on the exclusion list on the stack
 815  
         /*
 816  
         Iterator<String> it = mctx.getExcludedValidatorIds();
 817  
         if (it != null)
 818  
         {            
 819  
             while (it.hasNext())
 820  
             {
 821  
                 String excludedId = it.next();
 822  
                 if (excludedId.equals(validatorId))
 823  
                 {
 824  
                     return false;
 825  
                 }
 826  
             }
 827  
         }*/
 828  0
         Iterator<Map.Entry<String, EditableValueHolderAttachedObjectHandler>> enclosingValidatorIds =
 829  
             mctx.getEnclosingValidatorIdsAndHandlers();
 830  0
         if (enclosingValidatorIds != null)
 831  
         {
 832  0
             while (enclosingValidatorIds.hasNext())
 833  
             {
 834  0
                 Map.Entry<String, EditableValueHolderAttachedObjectHandler> entry = enclosingValidatorIds.next();
 835  0
                 boolean validatorIdAvailable = entry.getKey() != null && !"".equals(entry.getKey());
 836  0
                 if (validatorIdAvailable && entry.getKey().equals(validatorId))
 837  
                 {
 838  0
                     if (((ValidatorHandler)((FacesWrapper<ValidatorHandler>)entry.getValue()).getWrapped())
 839  
                             .isDisabled(ctx))
 840  
                     {
 841  0
                         return false;
 842  
                     }
 843  
                 }
 844  0
             }
 845  
         }
 846  
         
 847  
         // Some extra rules are required for Bean Validation.
 848  0
         if (validatorId.equals(BeanValidator.VALIDATOR_ID))
 849  
         {
 850  0
             if (!ExternalSpecifications.isBeanValidationAvailable())
 851  
             {
 852  
                 // the BeanValidator was added as a default-validator, but
 853  
                 // bean validation is not available on the classpath.
 854  
                 // --> log a warning about this scenario.
 855  0
                 log.log(Level.WARNING, "Bean validation is not available on the " +
 856  
                         "classpath, thus the BeanValidator will not be added for " +
 857  
                         "the component " + component);
 858  0
                 return false;
 859  
             }
 860  
         }
 861  
 
 862  
         // By default, all default validators should be added
 863  0
         return true;
 864  
     }
 865  
 
 866  
     private void addEnclosingValidator(FacesContext context, 
 867  
             EditableValueHolder component, String validatorId, 
 868  
             EditableValueHolderAttachedObjectHandler attachedObjectHandler)
 869  
     {
 870  0
         if (shouldAddEnclosingValidator(component, validatorId))
 871  
         {
 872  0
             if (attachedObjectHandler != null)
 873  
             {
 874  0
                 attachedObjectHandler.applyAttachedObject(context, (UIComponent) component);
 875  
             }
 876  
             else
 877  
             {
 878  0
                 Validator validator = null;
 879  
                 // create it
 880  0
                 validator = context.getApplication().createValidator(validatorId);
 881  
 
 882  
                 // special things to configure for a BeanValidator
 883  0
                 if (validator instanceof BeanValidator)
 884  
                 {
 885  0
                     BeanValidator beanValidator = (BeanValidator) validator;
 886  
                     
 887  
                     // check the validationGroups
 888  0
                     String validationGroups =  beanValidator.getValidationGroups();
 889  0
                     if (validationGroups == null 
 890  
                             || validationGroups.matches(BeanValidator.EMPTY_VALIDATION_GROUPS_PATTERN))
 891  
                     {
 892  
                         // no validationGroups available
 893  
                         // --> get the validationGroups from the stack
 894  
                         //String stackGroup = mctx.getFirstValidationGroupFromStack();
 895  
                         //if (stackGroup != null)
 896  
                         //{
 897  
                         //    validationGroups = stackGroup;
 898  
                         //}
 899  
                         //else
 900  
                         //{
 901  
                             // no validationGroups on the stack
 902  
                             // --> set the default validationGroup
 903  0
                             validationGroups = javax.validation.groups.Default.class.getName();
 904  
                         //}
 905  0
                         beanValidator.setValidationGroups(validationGroups);
 906  
                     }
 907  
                 }
 908  
                 
 909  
                 // add the validator to the component
 910  0
                 component.addValidator(validator);
 911  
             }
 912  
         }
 913  0
     }
 914  
 
 915  
     /**
 916  
      * Determine if the validator with the given validatorId should be added.
 917  
      * 
 918  
      * The difference here with shouldAddEnclosingValidator is the inner one has
 919  
      * precedence over the outer one, so a disable="true" over the same outer 
 920  
      * validator, the inner one should ignore this condition. 
 921  
      * 
 922  
      * @param mctx
 923  
      * @param facesContext
 924  
      * @param component
 925  
      * @param validatorId
 926  
      * @return
 927  
      */
 928  
     @SuppressWarnings("unchecked")
 929  
     private boolean shouldAddEnclosingValidator(
 930  
             EditableValueHolder component, 
 931  
             String validatorId)
 932  
     {
 933  
         // check if the validatorId is on the exclusion list on the component
 934  0
         List<String> exclusionList = (List<String>) ((UIComponent) component)
 935  
                 .getAttributes()
 936  
                 .get(ValidatorTagHandlerDelegate.VALIDATOR_ID_EXCLUSION_LIST_KEY);
 937  0
         if (exclusionList != null)
 938  
         {
 939  0
             for (String excludedId : exclusionList)
 940  
             {
 941  0
                 if (excludedId.equals(validatorId))
 942  
                 {
 943  0
                     return false;
 944  
                 }
 945  0
             }
 946  
         }
 947  
 
 948  
         // Some extra rules are required for Bean Validation.
 949  0
         if (validatorId.equals(BeanValidator.VALIDATOR_ID) &&
 950  
             !ExternalSpecifications.isBeanValidationAvailable())
 951  
         {
 952  
             // the BeanValidator was added as a default-validator, but
 953  
             // bean validation is not available on the classpath.
 954  
             // --> log a warning about this scenario.
 955  0
             log.log(Level.WARNING,
 956  
                     "Bean validation is not available on the "
 957  
                             + "classpath, thus the BeanValidator will not be added for "
 958  
                             + "the component " + component);
 959  0
             return false;
 960  
         }
 961  
 
 962  
         // By default, all default validators should be added
 963  0
         return true;
 964  
     }
 965  
     
 966  0
     private static class PublishFaceletDynamicComponentRefreshTransientBuildCallback implements VisitCallback
 967  
     {
 968  
         public VisitResult visit(VisitContext context, UIComponent target)
 969  
         {
 970  0
             context.getFacesContext().getApplication().publishEvent(
 971  
                     context.getFacesContext(), FaceletDynamicComponentRefreshTransientBuildEvent.class, 
 972  
                     target.getClass(), target);
 973  0
             return VisitResult.ACCEPT;
 974  
         }
 975  
     }
 976  
 }