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.io.StringWriter;
22  import javax.faces.component.UICommand;
23  import javax.faces.component.UIComponent;
24  import javax.faces.context.FacesContext;
25  import javax.faces.context.ResponseWriter;
26  import javax.faces.convert.ConverterException;
27  import javax.faces.render.Renderer;
28  
29  /***
30   * <p>This Renderer is a hybrid renderer decorator that is dynamically
31   * registered by {@link ValidatorRenderKit}
32   * for component renderers in the "javax.faces.Command" family.</p>
33   */
34  public class ValidatorCommandRenderer extends Renderer {
35  
36  
37      /***
38       * <p>The Original Renderer.</p>
39       */
40      private Renderer defaultRenderer = null;
41  
42  
43      /***
44       * <p>The overloaded constructor is passed the original
45       * <code>Renderer</code> for the family and component type.</p>
46       *
47       * @param defaultRenderer The default Renderer we should delegate to
48       */
49      public ValidatorCommandRenderer(Renderer defaultRenderer) {
50         this.defaultRenderer = defaultRenderer;
51      }
52  
53  
54      /***
55       * <p>Attribute name used to override the default behavior of how the immediate
56       * attribute effects the execution of client side javascript validation.</p>
57       */
58      public static final String OVERRIDE_IMMEDIATE = "org.apache.shale.validator.immediate";
59  
60      private static final int ENCODE_BEGIN = 0;
61  
62      private static final int ENCODE_CHILDREN = 1;
63  
64      private static final int ENCODE_END = 2;
65  
66  
67      /***
68       * <b>Interrogates the component's immediate property and the component's
69       * immediate override attribute to determine if client side validation is
70       * invoked. If either the property or attribute override is false, client
71       * side validation is invoked. Otherwise, the response writer is hijacked
72       * and the original render is invoked. The result is buffered and a
73       * statement of javascript is injected into the onclick event which cancels
74       * client side validation. The original response writer is restored and the
75       * modified markup is written to the response writer. The
76       * <code>encodeSwitch</code> determines if the encodeBegin, encodeChildren
77       * or encodeEnd methods should be invoked on the decorated renderer.</b>
78       *
79       * @param context FacesContext for the current request
80       * @param component UIComponent being rendered
81       * @param encodeSwitch FIXME - encode switch?
82       *
83       * @exception IOException if an input/output error occurs
84       */
85      protected void encode(FacesContext context, UIComponent component,
86              int encodeSwitch) throws IOException {
87  
88          UICommand command = (UICommand) component;
89  
90          // look for a override to the default
91          boolean immediateOverride = true;
92          String attr = (String) component.getAttributes()
93                  .get(OVERRIDE_IMMEDIATE);
94          if (attr != null) {
95              immediateOverride = Boolean.valueOf(attr).booleanValue();
96          }
97  
98          if (command.isImmediate() && immediateOverride) {
99  
100             ResponseWriter hijackedWriter = context.getResponseWriter();
101             // builds a buffer to write the page to
102             StringWriter writer = new StringWriter();
103             // create a buffered response writer
104             ResponseWriter buffResponsewriter = context.getRenderKit()
105                     .createResponseWriter(writer, null,
106                             hijackedWriter.getCharacterEncoding());
107             // push buffered writer to the faces context
108             context.setResponseWriter(buffResponsewriter);
109 
110             if (encodeSwitch == ENCODE_BEGIN) {
111                 defaultRenderer.encodeBegin(context, component);
112             } else if (encodeSwitch == ENCODE_CHILDREN) {
113                 defaultRenderer.encodeChildren(context, component);
114             } else {
115                 defaultRenderer.encodeEnd(context, component);
116             }
117 
118             buffResponsewriter.write(' ');
119             buffResponsewriter.flush();
120             buffResponsewriter.close();
121             writer.flush();
122             writer.close();
123             StringBuffer buff = writer.getBuffer();
124             int i = buff.indexOf("onclick=\"");
125             if (i > 0) {
126                 buff.insert(i + "onclick=\"".length(), "bCancel=true;");
127             }
128 
129             hijackedWriter.write(buff.toString());
130             context.setResponseWriter(hijackedWriter);
131 
132         } else {
133 
134             if (encodeSwitch == ENCODE_BEGIN) {
135                 defaultRenderer.encodeBegin(context, component);
136             } else if (encodeSwitch == ENCODE_CHILDREN) {
137                 defaultRenderer.encodeChildren(context, component);
138             } else {
139                 defaultRenderer.encodeEnd(context, component);
140             }
141 
142         }
143 
144     }
145 
146 
147     /*** {@inheritDoc} */
148     public String convertClientId(FacesContext context, String id) {
149         return defaultRenderer.convertClientId(context, id);
150     }
151 
152 
153     /*** {@inheritDoc} */
154     public Object getConvertedValue(FacesContext context, UIComponent component, Object o) throws ConverterException {
155         return defaultRenderer.getConvertedValue(context, component, o);
156     }
157 
158 
159     /*** {@inheritDoc} */
160     public void decode(FacesContext context, UIComponent component) {
161         defaultRenderer.decode(context, component);
162     }
163 
164 
165     /***
166      * <p>
167      * Invokes the <code>encode</code> method passing
168      * <code>ENCODE_BEGIN</code> for the encodeSwitch parameter.
169      * </p>
170      *
171      * @param context FacesContext for the current request
172      * @param component UIComponent being rendered
173      *
174      * @exception IOException if an input/output error occurs
175      */
176     public void encodeBegin(FacesContext context, UIComponent component)
177             throws IOException {
178         encode(context, component, ENCODE_BEGIN);
179     }
180 
181 
182     /***
183      * <p>Invokes the <code>encode</code> method passing
184      * <code>ENCODE_CHILDREN</code> for the encodeSwitch parameter.</p>
185      *
186      * @param context FacesContext for the current request
187      * @param component UIComponent being rendered
188      *
189      * @exception IOException if an input/output error occurs
190      */
191     public void encodeChildren(FacesContext context, UIComponent component)
192             throws IOException {
193         encode(context, component, ENCODE_CHILDREN);
194     }
195 
196 
197     /***
198      * <p>Invokes the <code>encode</code> method passing <code>ENCODE_END</code>
199      * for the encodeSwitch parameter.</p>
200      *
201      * @param context FacesContext for the current reqauest
202      * @param component UIComponent being rendered
203      *
204      * @exception IOException if an input/output error occurs
205      */
206     public void encodeEnd(FacesContext context, UIComponent component)
207             throws IOException {
208         encode(context, component, ENCODE_END);
209     }
210 
211 
212     /*** {@inheritDoc} */
213     public boolean getRendersChildren() {
214         return defaultRenderer.getRendersChildren();
215     }
216 
217 
218 }