View Javadoc

1   /*
2    * Copyright 2002-2004 The Apache Software Foundation.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.apache.struts.faces.renderer;
18  
19  
20  import java.io.IOException;
21  import java.util.Iterator;
22  
23  import javax.faces.component.NamingContainer;
24  import javax.faces.component.UICommand;
25  import javax.faces.component.UIComponent;
26  import javax.faces.component.UIForm;
27  import javax.faces.component.UIParameter;
28  import javax.faces.context.FacesContext;
29  import javax.faces.context.ResponseWriter;
30  import javax.faces.event.ActionEvent;
31  
32  import org.apache.commons.logging.Log;
33  import org.apache.commons.logging.LogFactory;
34  import org.apache.struts.Globals;
35  import org.apache.struts.config.ActionConfig;
36  import org.apache.struts.config.ModuleConfig;
37  
38  
39  /***
40   * <p><code>Renderer</code> implementation for the <code>commandLink</code>
41   * tag from the <em>Struts-Faces Integration Library</em>.</p>
42   *
43   * @version $Rev: 421138 $ $Date: 2006-07-11 22:41:40 -0700 (Tue, 11 Jul 2006) $
44   */
45  
46  public class CommandLinkRenderer extends AbstractRenderer {
47  
48  
49      // -------------------------------------------------------- Static Variables
50  
51  
52      /***
53       * <p>Token for private names.</p>
54       */
55      private static final String TOKEN =
56          "org_apache_struts_faces_renderer_CommandLinkRenderer";
57  
58  
59      /***
60       * <p>The <code>Log</code> instance for this class.</p>
61       */
62      private static Log log = LogFactory.getLog(CommandLinkRenderer.class);
63  
64  
65      // ---------------------------------------------------------- Public Methods
66  
67  
68      /***
69       * <p>Perform setup processing that will be required for decoding the
70       * incoming request.</p>
71       *
72       * @param context FacesContext for the request we are processing
73       * @param component UIComponent to be processed
74       *
75       * @exception NullPointerException if <code>context</code>
76       *  or <code>component</code> is null
77       */
78      public void decode(FacesContext context, UIComponent component) {
79  
80          // Implement spec requirements on NullPointerException
81          if ((context == null) || (component == null)) {
82              throw new NullPointerException();
83          }
84  
85          // Skip this component if it is not relevant
86          if (!component.isRendered() || isDisabled(component) ||
87              isReadOnly(component)) {
88              return;
89          }
90  
91          // Set up variables we will need
92          UIForm form = null;
93          UIComponent parent = component.getParent();
94          while (parent != null) {
95              if (parent instanceof UIForm) {
96                  form = (UIForm) parent;
97                  break;
98              }
99              parent = parent.getParent();
100         }
101         if (form == null) {
102             log.warn("CommandLinkComponent not nested inside UIForm, ignored");
103             return;
104         }
105 
106         // Was this the component that submitted this form?
107         String paramId = TOKEN;
108         String value = (String)
109             context.getExternalContext().getRequestParameterMap().get(paramId);
110         if ((value == null) || !value.equals(component.getClientId(context))) {
111             if (log.isTraceEnabled()) {
112                 log.trace("decode(" + component.getId() + ") --> not active");
113             }
114             return;
115         }
116 
117         // Queue an ActionEvent from this component
118         if (log.isTraceEnabled()) {
119             log.trace("decode(" + component.getId() + ") --> queueEvent()");
120         }
121         component.queueEvent(new ActionEvent(component));
122 
123     }
124 
125 
126     private static String passThrough[] =
127     { "accesskey", "charset", "dir", "hreflang", "lang", "onblur",
128       /* "onclick", */ "ondblclick", "onfocus", "onkeydown",
129       "onkeypress", "onkeyup", "onmousedown", "onmousemove",
130       "onmouseout", "onmouseover", "onmouseup", "rel", "rev",
131       "style", "tabindex", "target", "title", "type" };
132 
133 
134     /***
135      * <p>Render the beginning of a hyperlink to submit this form.</p>
136      *
137      * @param context FacesContext for the request we are processing
138      * @param component UIComponent to be rendered
139      * @param writer ResponseWriter we are rendering to
140      *
141      * @exception IOException if an input/output error occurs while rendering
142      * @exception NullPointerException if <code>context</code>
143      *  or <code>component</code> is null
144      */
145     public void renderStart(FacesContext context, UIComponent component,
146                             ResponseWriter writer)
147         throws IOException {
148 
149         // Skip this component if it is not relevant
150         if (!component.isRendered() || isDisabled(component) ||
151             isReadOnly(component)) {
152             return;
153         }
154 
155         // Set up variables we will need
156         UIForm form = null;
157         UIComponent parent = component.getParent();
158         while (parent != null) {
159             if (parent instanceof UIForm) {
160                 form = (UIForm) parent;
161                 break;
162             }
163             parent = parent.getParent();
164         }
165         if (form == null) {
166             log.warn("CommandLinkComponent not nested inside UIForm, ignored");
167             return;
168         }
169         String formClientId = form.getClientId(context);
170 
171         // If this is the first nested command link inside this form,
172         // render a hidden variable to identify which link did the submit
173         String key = formClientId + NamingContainer.SEPARATOR_CHAR + TOKEN;
174         if (context.getExternalContext().getRequestMap().get(key) == null) {
175             writer.startElement("input", null);
176             writer.writeAttribute("name", TOKEN, null);
177             writer.writeAttribute("type", "hidden", null);
178             writer.writeAttribute("value", "", null);
179             writer.endElement("input");
180             context.getExternalContext().getRequestMap().put
181                 (key, Boolean.TRUE);
182         }
183 
184 
185         // Render the beginning of this hyperlink
186         writer.startElement("a", component);
187 
188     }
189 
190 
191     /***
192      * <p>Render the attributes of a hyperlink to submit this form.</p>
193      *
194      * @param context FacesContext for the request we are processing
195      * @param component UIComponent to be rendered
196      * @param writer ResponseWriter we are rendering to
197      *
198      * @exception IOException if an input/output error occurs while rendering
199      * @exception NullPointerException if <code>context</code>
200      *  or <code>component</code> is null
201      */
202     public void renderAttributes(FacesContext context, UIComponent component,
203                                  ResponseWriter writer)
204         throws IOException {
205 
206         // Skip this component if it is not relevant
207         if (!component.isRendered() || isDisabled(component) ||
208             isReadOnly(component)) {
209             return;
210         }
211 
212         // Set up variables we will need
213         UIForm form = null;
214         UIComponent parent = component.getParent();
215         while (parent != null) {
216             if (parent instanceof UIForm) {
217                 form = (UIForm) parent;
218                 break;
219             }
220             parent = parent.getParent();
221         }
222         if (form == null) {
223             log.warn("CommandLinkComponent not nested inside UIForm, ignored");
224             return;
225         }
226         String formClientId = form.getClientId(context);
227 
228         // Render the attributes of this hyperlink
229         if (component.getId() != null) {
230             writer.writeAttribute("id", component.getClientId(context), "id");
231         }
232         writer.writeAttribute("href", "#", null);
233         String styleClass = (String)
234             component.getAttributes().get("styleClass");
235         if (styleClass != null) {
236             writer.writeAttribute("class", styleClass, "styleClass");
237         }
238         renderPassThrough(context, component, writer, passThrough);
239 
240         // Render the JavaScript content of the "onclick" element
241         StringBuffer sb = new StringBuffer();
242         sb.append("document.forms['");
243         sb.append(formClientId);
244         sb.append("']['");
245         sb.append(TOKEN);
246         sb.append("'].value='");
247         sb.append(component.getClientId(context));
248         sb.append("';");
249         Iterator kids = component.getChildren().iterator();
250         while (kids.hasNext()) {
251             UIComponent kid = (UIComponent) kids.next();
252             if (!(kid instanceof UIParameter)) {
253                 continue;
254             }
255             sb.append("document.forms['");
256             sb.append(formClientId);
257             sb.append("']['");
258             sb.append((String) kid.getAttributes().get("name"));
259             sb.append("'].value='");
260             sb.append((String) kid.getAttributes().get("value"));
261             sb.append("';");
262         }
263         sb.append("document.forms['");
264         sb.append(formClientId);
265         sb.append("'].submit(); return false;");
266         writer.writeAttribute("onclick", sb.toString(), null);
267 
268         // Render the component value as the hyperlink text
269         Object value = component.getAttributes().get("value");
270         if (value != null) {
271             if (value instanceof String) {
272                 writer.write((String) value);
273             } else {
274                 writer.write(value.toString());
275             }
276         }
277 
278     }
279 
280 
281     /***
282      * <p>Render the end of a hyperlink to submit this form.</p>
283      *
284      * @param context FacesContext for the request we are processing
285      * @param component UIComponent to be rendered
286      * @param writer ResponseWriter we are rendering to
287      *
288      * @exception IOException if an input/output error occurs while rendering
289      * @exception NullPointerException if <code>context</code>
290      *  or <code>component</code> is null
291      */
292     public void renderEnd(FacesContext context, UIComponent component,
293                           ResponseWriter writer)
294         throws IOException {
295 
296         // Skip this component if it is not relevant
297         if (!component.isRendered() || isDisabled(component) ||
298             isReadOnly(component)) {
299             return;
300         }
301 
302         // Render the beginning of this hyperlink
303         writer.endElement("a");
304 
305     }
306 
307 
308 }