Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||||||
ValidatorInputRenderer |
|
| 2.3333333333333335;2.333 |
1 | /* |
|
2 | * Licensed to the Apache Software Foundation (ASF) under one or more |
|
3 | * contributor license agreements. See the NOTICE file distributed with |
|
4 | * this work for additional information regarding copyright ownership. |
|
5 | * The ASF licenses this file to you under the Apache License, Version 2.0 |
|
6 | * (the "License"); you may not use this file except in compliance with |
|
7 | * the License. You may obtain a copy of the License at |
|
8 | * |
|
9 | * http://www.apache.org/licenses/LICENSE-2.0 |
|
10 | * |
|
11 | * Unless required by applicable law or agreed to in writing, software |
|
12 | * distributed under the License is distributed on an "AS IS" BASIS, |
|
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
14 | * See the License for the specific language governing permissions and |
|
15 | * limitations under the License. |
|
16 | */ |
|
17 | ||
18 | package org.apache.shale.validator.faces; |
|
19 | ||
20 | import java.io.IOException; |
|
21 | import java.util.Iterator; |
|
22 | import java.util.Map; |
|
23 | import java.util.TreeMap; |
|
24 | import javax.faces.component.EditableValueHolder; |
|
25 | import javax.faces.component.UIComponent; |
|
26 | import javax.faces.context.FacesContext; |
|
27 | import javax.faces.convert.ConverterException; |
|
28 | import javax.faces.render.Renderer; |
|
29 | import javax.faces.validator.Validator; |
|
30 | import org.apache.shale.util.Tags; |
|
31 | import org.apache.shale.validator.CommonsValidator; |
|
32 | ||
33 | ||
34 | /** |
|
35 | * <p>This renderer is a hybrid renderer decorator that is dynamically |
|
36 | * registered by the {@link ValidatorRenderKit} |
|
37 | * for component renderers in the "javax.faces.Input" family.</p> |
|
38 | */ |
|
39 | public class ValidatorInputRenderer extends Renderer { |
|
40 | ||
41 | ||
42 | /** |
|
43 | * <p>The Renderer that we are wrapping.</p> |
|
44 | */ |
|
45 | 0 | private Renderer defaultRenderer = null; |
46 | ||
47 | ||
48 | /** |
|
49 | * <p>This constant is the name of a reserved attribute that will hold |
|
50 | * a <code>Map</code> of clientId's for the component.</p> |
|
51 | */ |
|
52 | public static final String VALIDATOR_CLIENTIDS_ATTR = "org.apache.shale.validator.clientIdSet"; |
|
53 | ||
54 | ||
55 | /** |
|
56 | * <p>Overloaded constructor is passed the original |
|
57 | * <code>Renderer</code>.</p> |
|
58 | * |
|
59 | * @param defaultRenderer The Renderer we should wrap |
|
60 | */ |
|
61 | 0 | public ValidatorInputRenderer(Renderer defaultRenderer) { |
62 | 0 | this.defaultRenderer = defaultRenderer; |
63 | 0 | } |
64 | ||
65 | ||
66 | /** {@inheritDoc} */ |
|
67 | public String convertClientId(FacesContext context, String id) { |
|
68 | 0 | return defaultRenderer.convertClientId(context, id); |
69 | } |
|
70 | ||
71 | ||
72 | /** {@inheritDoc} */ |
|
73 | public void decode(FacesContext context, UIComponent component) { |
|
74 | 0 | defaultRenderer.decode(context, component); |
75 | 0 | } |
76 | ||
77 | ||
78 | /** |
|
79 | * <p>This override captures the clientId of the target component before |
|
80 | * passing on to the original renderer. The clientId is added to a Map |
|
81 | * that is used by the {@link org.apache.shale.component.ValidatorScript} |
|
82 | * component for adding client side JavaScript validation. This hook is |
|
83 | * needed when the {@link org.apache.shale.validator.CommonsValidator} |
|
84 | * is added to a UIData subclass. The components in this class are not |
|
85 | * unique per row so the clientId can only be captured during the rendering |
|
86 | * process. The Map also contains a snapshot of validator var arguments |
|
87 | * that contain value binding expressions. This snapshot of state at |
|
88 | * renderering is used by the client side JavaScript. The snapshot |
|
89 | * allows client side validation in UIData components.</p> |
|
90 | * |
|
91 | * @param context FacesContext for the current request |
|
92 | * @param component UIComponent being rendered |
|
93 | * |
|
94 | * @exception IOException if an input/output error occurs |
|
95 | */ |
|
96 | public void encodeBegin(FacesContext context, UIComponent component) throws IOException { |
|
97 | ||
98 | 0 | if (component instanceof EditableValueHolder && component.isRendered()) { |
99 | ||
100 | 0 | Tags tagUtils = new Tags(); |
101 | ||
102 | 0 | EditableValueHolder editableComponent = (EditableValueHolder) component; |
103 | ||
104 | // A map that captures information about a component that might contain |
|
105 | // commons validators. The map is organized by a hierarchy "clientId/validatorType/vars" |
|
106 | 0 | Map ids = (Map) component.getAttributes().get(VALIDATOR_CLIENTIDS_ATTR); |
107 | 0 | if (ids == null) { |
108 | 0 | ids = new TreeMap(); |
109 | 0 | component.getAttributes().put(VALIDATOR_CLIENTIDS_ATTR, ids); |
110 | } |
|
111 | ||
112 | // captrue the clientId before renderering |
|
113 | 0 | String clientId = component.getClientId(context); |
114 | 0 | Map validatorVars = (Map) ids.get(clientId); |
115 | 0 | if (validatorVars == null) { |
116 | 0 | validatorVars = new TreeMap(); |
117 | 0 | ids.put(clientId, validatorVars); |
118 | } |
|
119 | ||
120 | ||
121 | 0 | Validator[] validators = editableComponent.getValidators(); |
122 | // look for components using CommonsValidator |
|
123 | 0 | for (int i = 0; i < validators.length; i++) { |
124 | 0 | if (validators[i] instanceof CommonsValidator) { |
125 | 0 | CommonsValidator validator = (CommonsValidator) validators[i]; |
126 | ||
127 | // look for a map of var's by component type |
|
128 | 0 | Map localVars = (Map) validatorVars.get(validator.getType()); |
129 | 0 | if (localVars == null) { |
130 | 0 | localVars = new TreeMap(); |
131 | 0 | validatorVars.put(validator.getType(), localVars); |
132 | 0 | } else { |
133 | 0 | localVars.clear(); |
134 | } |
|
135 | ||
136 | 0 | Map vars = validator.getVars(); |
137 | 0 | Iterator vi = vars.entrySet().iterator(); |
138 | 0 | while (vi.hasNext()) { |
139 | 0 | Map.Entry e = (Map.Entry) vi.next(); |
140 | // only override if the var contains a value binding expression |
|
141 | 0 | if (e.getValue() != null && e.getValue() instanceof String |
142 | && isValueReference((String) e.getValue())) { |
|
143 | ||
144 | 0 | localVars.put(e.getKey(), tagUtils.eval((String) e.getValue())); |
145 | ||
146 | } |
|
147 | 0 | } |
148 | ||
149 | } |
|
150 | } |
|
151 | ||
152 | } |
|
153 | ||
154 | 0 | defaultRenderer.encodeBegin(context, component); |
155 | 0 | } |
156 | ||
157 | ||
158 | /** {@inheritDoc} */ |
|
159 | public void encodeChildren(FacesContext context, UIComponent component) throws IOException { |
|
160 | 0 | defaultRenderer.encodeChildren(context, component); |
161 | 0 | } |
162 | ||
163 | ||
164 | /** {@inheritDoc} */ |
|
165 | public void encodeEnd(FacesContext context, UIComponent component) throws IOException { |
|
166 | 0 | defaultRenderer.encodeEnd(context, component); |
167 | 0 | } |
168 | ||
169 | ||
170 | /** {@inheritDoc} */ |
|
171 | public Object getConvertedValue(FacesContext context, UIComponent component, |
|
172 | Object value) throws ConverterException { |
|
173 | 0 | return defaultRenderer.getConvertedValue(context, component, value); |
174 | } |
|
175 | ||
176 | ||
177 | /** {@inheritDoc} */ |
|
178 | public boolean getRendersChildren() { |
|
179 | 0 | return defaultRenderer.getRendersChildren(); |
180 | } |
|
181 | ||
182 | ||
183 | /** |
|
184 | * <p>Return true if the specified string contains an EL expression.</p> |
|
185 | * |
|
186 | * <p>This is taken almost verbatim from {@link javax.faces.webapp.UIComponentTag} |
|
187 | * in order to remove JSP dependencies from the renderers.</p> |
|
188 | * |
|
189 | * @param value String to be checked for being an expression |
|
190 | */ |
|
191 | private boolean isValueReference(String value) { |
|
192 | ||
193 | 0 | if (value == null) { |
194 | 0 | return false; |
195 | } |
|
196 | ||
197 | 0 | int start = value.indexOf("#{"); |
198 | 0 | if (start < 0) { |
199 | 0 | return false; |
200 | } |
|
201 | ||
202 | 0 | int end = value.lastIndexOf('}'); |
203 | 0 | return (end >= 0) && (start < end); |
204 | } |
|
205 | ||
206 | ||
207 | } |