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