2009/05/20 - Apache Shale has been retired.

For more information, please explore the Attic.

View Javadoc

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      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                 // 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                 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                 // captrue the clientId before renderering
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                 // look for components using CommonsValidator
123                 for (int i = 0; i < validators.length; i++) {
124                     if (validators[i] instanceof CommonsValidator) {
125                        CommonsValidator validator = (CommonsValidator) validators[i];
126 
127                        // look for a map of var's by component type
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                           // only override if the var contains a value binding expression
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 }