2009/05/20 - Apache Shale has been retired.
For more information, please explore the Attic.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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 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 public ValidatorInputRenderer(Renderer defaultRenderer) {
62 this.defaultRenderer = defaultRenderer;
63 }
64
65
66 /*** {@inheritDoc} */
67 public String convertClientId(FacesContext context, String id) {
68 return defaultRenderer.convertClientId(context, id);
69 }
70
71
72 /*** {@inheritDoc} */
73 public void decode(FacesContext context, UIComponent component) {
74 defaultRenderer.decode(context, component);
75 }
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 if (component instanceof EditableValueHolder && component.isRendered()) {
99
100 Tags tagUtils = new Tags();
101
102 EditableValueHolder editableComponent = (EditableValueHolder) component;
103
104
105
106 Map ids = (Map) component.getAttributes().get(VALIDATOR_CLIENTIDS_ATTR);
107 if (ids == null) {
108 ids = new TreeMap();
109 component.getAttributes().put(VALIDATOR_CLIENTIDS_ATTR, ids);
110 }
111
112
113 String clientId = component.getClientId(context);
114 Map validatorVars = (Map) ids.get(clientId);
115 if (validatorVars == null) {
116 validatorVars = new TreeMap();
117 ids.put(clientId, validatorVars);
118 }
119
120
121 Validator[] validators = editableComponent.getValidators();
122
123 for (int i = 0; i < validators.length; i++) {
124 if (validators[i] instanceof CommonsValidator) {
125 CommonsValidator validator = (CommonsValidator) validators[i];
126
127
128 Map localVars = (Map) validatorVars.get(validator.getType());
129 if (localVars == null) {
130 localVars = new TreeMap();
131 validatorVars.put(validator.getType(), localVars);
132 } else {
133 localVars.clear();
134 }
135
136 Map vars = validator.getVars();
137 Iterator vi = vars.entrySet().iterator();
138 while (vi.hasNext()) {
139 Map.Entry e = (Map.Entry) vi.next();
140
141 if (e.getValue() != null && e.getValue() instanceof String
142 && isValueReference((String) e.getValue())) {
143
144 localVars.put(e.getKey(), tagUtils.eval((String) e.getValue()));
145
146 }
147 }
148
149 }
150 }
151
152 }
153
154 defaultRenderer.encodeBegin(context, component);
155 }
156
157
158 /*** {@inheritDoc} */
159 public void encodeChildren(FacesContext context, UIComponent component) throws IOException {
160 defaultRenderer.encodeChildren(context, component);
161 }
162
163
164 /*** {@inheritDoc} */
165 public void encodeEnd(FacesContext context, UIComponent component) throws IOException {
166 defaultRenderer.encodeEnd(context, component);
167 }
168
169
170 /*** {@inheritDoc} */
171 public Object getConvertedValue(FacesContext context, UIComponent component,
172 Object value) throws ConverterException {
173 return defaultRenderer.getConvertedValue(context, component, value);
174 }
175
176
177 /*** {@inheritDoc} */
178 public boolean getRendersChildren() {
179 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 if (value == null) {
194 return false;
195 }
196
197 int start = value.indexOf("#{");
198 if (start < 0) {
199 return false;
200 }
201
202 int end = value.lastIndexOf('}');
203 return (end >= 0) && (start < end);
204 }
205
206
207 }