Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
ValidatorTagHandlerDelegate |
|
| 3.7777777777777777;3.778 |
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.ArrayList; | |
23 | import java.util.List; | |
24 | ||
25 | import javax.el.ValueExpression; | |
26 | import javax.faces.FacesWrapper; | |
27 | import javax.faces.component.EditableValueHolder; | |
28 | import javax.faces.component.UIComponent; | |
29 | import javax.faces.context.FacesContext; | |
30 | import javax.faces.validator.Validator; | |
31 | import javax.faces.view.EditableValueHolderAttachedObjectHandler; | |
32 | import javax.faces.view.facelets.ComponentHandler; | |
33 | import javax.faces.view.facelets.FaceletContext; | |
34 | import javax.faces.view.facelets.MetaRuleset; | |
35 | import javax.faces.view.facelets.TagAttribute; | |
36 | import javax.faces.view.facelets.TagException; | |
37 | import javax.faces.view.facelets.TagHandlerDelegate; | |
38 | import javax.faces.view.facelets.ValidatorHandler; | |
39 | import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFWebConfigParam; | |
40 | ||
41 | import org.apache.myfaces.shared.renderkit.JSFAttr; | |
42 | import org.apache.myfaces.shared.util.WebConfigParamUtils; | |
43 | import org.apache.myfaces.view.facelets.FaceletCompositionContext; | |
44 | import org.apache.myfaces.view.facelets.compiler.FaceletsCompilerUtils; | |
45 | import org.apache.myfaces.view.facelets.tag.MetaRulesetImpl; | |
46 | ||
47 | /** | |
48 | * Handles setting a Validator instance on a EditableValueHolder. Will wire all attributes set to the Validator instance | |
49 | * created/fetched. Uses the "binding" attribute for grabbing instances to apply attributes to. <p/> Will only | |
50 | * set/create Validator is the passed UIComponent's parent is null, signifying that it wasn't restored from an existing | |
51 | * tree. | |
52 | * | |
53 | * @author Leonardo Uribe (latest modification by $Author$) | |
54 | * @version $Revision$ $Date$ | |
55 | * | |
56 | * @since 2.0 | |
57 | */ | |
58 | 0 | public class ValidatorTagHandlerDelegate extends TagHandlerDelegate |
59 | implements EditableValueHolderAttachedObjectHandler, FacesWrapper<ValidatorHandler> | |
60 | { | |
61 | ||
62 | /** | |
63 | * if <f:validateBean> has no children and its disabled attribute is true, | |
64 | * its validatorId will be added to the exclusion list stored under | |
65 | * this key on the parent UIComponent. | |
66 | */ | |
67 | public final static String VALIDATOR_ID_EXCLUSION_LIST_KEY | |
68 | = "org.apache.myfaces.validator.VALIDATOR_ID_EXCLUSION_LIST"; | |
69 | ||
70 | /** | |
71 | * Enforce f:validateBean to be called first before any JSF validator. | |
72 | */ | |
73 | @JSFWebConfigParam(defaultValue="false", expectedValues="true, false", since = "2.2.10", group="validation") | |
74 | private final static String BEAN_BEFORE_JSF_VALIDATION | |
75 | = "org.apache.myfaces.validator.BEAN_BEFORE_JSF_VALIDATION"; | |
76 | ||
77 | private final static String BEAN_BEFORE_JSF_PROPERTY = "oam.beanBeforeJsf"; | |
78 | ||
79 | private ValidatorHandler _delegate; | |
80 | ||
81 | /** | |
82 | * true - this tag has children | |
83 | * false - this tag is a leave | |
84 | */ | |
85 | private final boolean _wrapMode; | |
86 | ||
87 | private Boolean _beanBeforeJsfValidation; | |
88 | ||
89 | public ValidatorTagHandlerDelegate(ValidatorHandler delegate) | |
90 | 0 | { |
91 | 0 | _delegate = delegate; |
92 | ||
93 | // According to jsf 2.0 spec section 10.4.1.4 | |
94 | // this tag can be used as a leave within an EditableValueHolder | |
95 | // or as a container to provide validator information for all | |
96 | // EditableValueHolder-children (and grandchildren and ...) | |
97 | // (this behavior is analog to <f:ajax>) | |
98 | // --> Determine if we have children: | |
99 | 0 | _wrapMode = FaceletsCompilerUtils.hasChildren(_delegate.getValidatorConfig()); |
100 | 0 | _beanBeforeJsfValidation = null; |
101 | 0 | } |
102 | ||
103 | @Override | |
104 | public void apply(FaceletContext ctx, UIComponent parent) throws IOException | |
105 | { | |
106 | // we need methods from AbstractFaceletContext | |
107 | 0 | FaceletCompositionContext mctx = FaceletCompositionContext.getCurrentInstance(ctx); |
108 | ||
109 | 0 | if (_wrapMode) |
110 | { | |
111 | // the tag has children --> provide validator information for all children | |
112 | ||
113 | // FIXME the spec says we should save the validation groups in an attribute | |
114 | // on the parent UIComponent, but this will be a problem in the following scenario: | |
115 | // <h:form> | |
116 | // <f:validateBean> | |
117 | // <h:inputText /> | |
118 | // </f:validateBean> | |
119 | // <h:inputText /> | |
120 | // </h:form> | |
121 | // because the validator would also be applied to the second h:inputText, | |
122 | // which it should not, on my opinion. In addition, mojarra also does not | |
123 | // attach the validator to the second h:inputText in this scenario (blackbox test). | |
124 | // So I use the same way as f:ajax for this problem. -=Jakob Korherr=- | |
125 | ||
126 | 0 | String validatorId = _delegate.getValidatorConfig().getValidatorId(); |
127 | /* | |
128 | boolean disabled = _delegate.isDisabled(ctx); | |
129 | if (disabled) | |
130 | { | |
131 | // the validator is disabled --> add its id to the exclusion stack | |
132 | boolean validatorIdAvailable = validatorId != null && !"".equals(validatorId); | |
133 | try | |
134 | { | |
135 | if (validatorIdAvailable) | |
136 | { | |
137 | mctx.pushExcludedValidatorIdToStack(validatorId); | |
138 | } | |
139 | _delegate.getValidatorConfig().getNextHandler().apply(ctx, parent); | |
140 | } | |
141 | finally | |
142 | { | |
143 | if (validatorIdAvailable) | |
144 | { | |
145 | mctx.popExcludedValidatorIdToStack(); | |
146 | } | |
147 | } | |
148 | } | |
149 | else | |
150 | {*/ | |
151 | // the validator is enabled | |
152 | // --> add the validation groups and the validatorId to the stack | |
153 | //String groups = getValidationGroups(ctx); | |
154 | // spec: don't save the validation groups string if it is null or empty string | |
155 | //boolean groupsAvailable = groups != null | |
156 | //&& !groups.matches(BeanValidator.EMPTY_VALIDATION_GROUPS_PATTERN); | |
157 | //try | |
158 | //{ | |
159 | //if (groupsAvailable) | |
160 | //{ | |
161 | // mctx.pushValidationGroupsToStack(groups); | |
162 | //} | |
163 | try | |
164 | { | |
165 | 0 | mctx.pushEnclosingValidatorIdToStack(validatorId, this); |
166 | 0 | _delegate.applyNextHandler(ctx, parent); |
167 | } | |
168 | finally | |
169 | { | |
170 | 0 | mctx.popEnclosingValidatorIdToStack(); |
171 | 0 | } |
172 | //} | |
173 | //finally | |
174 | //{ | |
175 | //if (groupsAvailable) | |
176 | //{ | |
177 | //mctx.popValidationGroupsToStack(); | |
178 | //} | |
179 | //} | |
180 | /*}*/ | |
181 | 0 | } |
182 | else | |
183 | { | |
184 | // Apply only if we are creating a new component | |
185 | 0 | if (!ComponentHandler.isNew(parent)) |
186 | { | |
187 | 0 | return; |
188 | } | |
189 | ||
190 | // the tag is a leave --> attach validator to parent | |
191 | 0 | if (parent instanceof EditableValueHolder) |
192 | { | |
193 | 0 | applyAttachedObject(ctx.getFacesContext(), parent); |
194 | } | |
195 | 0 | else if (UIComponent.isCompositeComponent(parent)) |
196 | { | |
197 | 0 | if (getFor() == null) |
198 | { | |
199 | 0 | throw new TagException(_delegate.getTag(), "is nested inside a composite component" |
200 | + " but does not have a for attribute."); | |
201 | } | |
202 | 0 | mctx.addAttachedObjectHandler(parent, _delegate); |
203 | } | |
204 | else | |
205 | { | |
206 | 0 | throw new TagException(_delegate.getTag(), |
207 | "Parent not composite component or an instance of EditableValueHolder: " + parent); | |
208 | } | |
209 | } | |
210 | 0 | } |
211 | ||
212 | /** | |
213 | * Template method for creating a Validator instance | |
214 | * | |
215 | * @param ctx FaceletContext to use | |
216 | * @return a new Validator instance | |
217 | */ | |
218 | protected Validator createValidator(FaceletContext ctx) | |
219 | { | |
220 | 0 | if (_delegate.getValidatorId(ctx) == null) |
221 | { | |
222 | 0 | throw new TagException(_delegate.getTag(), "Default behavior invoked of requiring " + |
223 | "a validator-id passed in the constructor, must override ValidateHandler(ValidatorConfig)"); | |
224 | } | |
225 | 0 | return ctx.getFacesContext().getApplication().createValidator(_delegate.getValidatorId(ctx)); |
226 | } | |
227 | ||
228 | @Override | |
229 | public MetaRuleset createMetaRuleset(Class type) | |
230 | { | |
231 | 0 | MetaRuleset metaRuleset = new MetaRulesetImpl(_delegate.getTag(), type); |
232 | ||
233 | // ignore binding and disabled, because they are handled by DelegatingMetaTagHandler | |
234 | 0 | metaRuleset.ignore(JSFAttr.BINDING_ATTR).ignore(JSFAttr.DISABLED_ATTR); |
235 | // ignore for, because it is handled by FaceletsAttachedObjectHandler | |
236 | 0 | metaRuleset.ignore(JSFAttr.FOR_ATTR); |
237 | ||
238 | 0 | return metaRuleset; |
239 | } | |
240 | ||
241 | @SuppressWarnings("unchecked") | |
242 | public void applyAttachedObject(FacesContext context, UIComponent parent) | |
243 | { | |
244 | // Retrieve the current FaceletContext from FacesContext object | |
245 | 0 | FaceletContext faceletContext = (FaceletContext) context.getAttributes().get( |
246 | FaceletContext.FACELET_CONTEXT_KEY); | |
247 | ||
248 | // spec: if the disabled attribute is true, the validator should not be added. | |
249 | // in addition, the validatorId, if present, should be added to an exclusion | |
250 | // list on the parent component to prevent a default validator with the same | |
251 | // id from beeing registered on the component. | |
252 | 0 | if (_delegate.isDisabled(faceletContext)) |
253 | { | |
254 | // tag is disabled --> add its validatorId to the parent's exclusion list | |
255 | 0 | String validatorId = _delegate.getValidatorConfig().getValidatorId(); |
256 | 0 | if (validatorId != null && !"".equals(validatorId)) |
257 | { | |
258 | 0 | List<String> exclusionList = (List<String>) parent.getAttributes() |
259 | .get(VALIDATOR_ID_EXCLUSION_LIST_KEY); | |
260 | 0 | if (exclusionList == null) |
261 | { | |
262 | 0 | exclusionList = new ArrayList<String>(); |
263 | 0 | parent.getAttributes().put(VALIDATOR_ID_EXCLUSION_LIST_KEY, exclusionList); |
264 | } | |
265 | 0 | exclusionList.add(validatorId); |
266 | } | |
267 | 0 | } |
268 | else | |
269 | { | |
270 | // tag is enabled --> create the validator and attach it | |
271 | ||
272 | // cast to a ValueHolder | |
273 | 0 | EditableValueHolder evh = (EditableValueHolder) parent; |
274 | 0 | ValueExpression ve = null; |
275 | 0 | Validator v = null; |
276 | 0 | if (_delegate.getBinding() != null) |
277 | { | |
278 | 0 | ve = _delegate.getBinding().getValueExpression(faceletContext, Validator.class); |
279 | 0 | v = (Validator) ve.getValue(faceletContext); |
280 | } | |
281 | 0 | if (v == null) |
282 | { | |
283 | 0 | v = this.createValidator(faceletContext); |
284 | 0 | if (ve != null) |
285 | { | |
286 | 0 | ve.setValue(faceletContext, v); |
287 | } | |
288 | } | |
289 | 0 | if (v == null) |
290 | { | |
291 | 0 | throw new TagException(_delegate.getTag(), "No Validator was created"); |
292 | } | |
293 | 0 | _delegate.setAttributes(faceletContext, v); |
294 | 0 | if (shouldBeanBeforeJsfValidationEnabled(context)) |
295 | { | |
296 | 0 | parent.getAttributes().put(BEAN_BEFORE_JSF_PROPERTY, Boolean.TRUE); |
297 | } | |
298 | 0 | evh.addValidator(v); |
299 | } | |
300 | 0 | } |
301 | ||
302 | private boolean shouldBeanBeforeJsfValidationEnabled(FacesContext context) | |
303 | { | |
304 | 0 | if (_beanBeforeJsfValidation == null) |
305 | { | |
306 | 0 | _beanBeforeJsfValidation = WebConfigParamUtils.getBooleanInitParameter(context.getExternalContext(), |
307 | BEAN_BEFORE_JSF_VALIDATION, false); | |
308 | } | |
309 | 0 | return _beanBeforeJsfValidation; |
310 | } | |
311 | ||
312 | ||
313 | public String getFor() | |
314 | { | |
315 | 0 | TagAttribute forAttribute = _delegate.getTagAttribute("for"); |
316 | ||
317 | 0 | if (forAttribute == null) |
318 | { | |
319 | 0 | return null; |
320 | } | |
321 | else | |
322 | { | |
323 | 0 | return forAttribute.getValue(); |
324 | } | |
325 | } | |
326 | ||
327 | public String getValidationGroups(FaceletContext ctx) | |
328 | { | |
329 | 0 | TagAttribute attribute = _delegate.getTagAttribute("validationGroups"); |
330 | ||
331 | 0 | if (attribute == null) |
332 | { | |
333 | 0 | return null; |
334 | } | |
335 | else | |
336 | { | |
337 | 0 | return attribute.getValue(ctx); |
338 | } | |
339 | } | |
340 | ||
341 | public ValidatorHandler getWrapped() | |
342 | { | |
343 | 0 | return _delegate; |
344 | } | |
345 | ||
346 | } |