View Javadoc

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