001    package org.apache.myfaces.tobago.renderkit.html;
002    
003    /*
004     * Licensed to the Apache Software Foundation (ASF) under one or more
005     * contributor license agreements.  See the NOTICE file distributed with
006     * this work for additional information regarding copyright ownership.
007     * The ASF licenses this file to You under the Apache License, Version 2.0
008     * (the "License"); you may not use this file except in compliance with
009     * the License.  You may obtain a copy of the License at
010     *
011     *      http://www.apache.org/licenses/LICENSE-2.0
012     *
013     * Unless required by applicable law or agreed to in writing, software
014     * distributed under the License is distributed on an "AS IS" BASIS,
015     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
016     * See the License for the specific language governing permissions and
017     * limitations under the License.
018     */
019    
020    import org.apache.commons.logging.Log;
021    import org.apache.commons.logging.LogFactory;
022    import static org.apache.myfaces.tobago.TobagoConstants.ATTR_ACTION_LINK;
023    import static org.apache.myfaces.tobago.TobagoConstants.ATTR_ACTION_ONCLICK;
024    import static org.apache.myfaces.tobago.TobagoConstants.ATTR_DEFAULT_COMMAND;
025    import static org.apache.myfaces.tobago.TobagoConstants.ATTR_DISABLED;
026    import static org.apache.myfaces.tobago.TobagoConstants.ATTR_POPUP_CLOSE;
027    import static org.apache.myfaces.tobago.TobagoConstants.ATTR_TARGET;
028    import static org.apache.myfaces.tobago.TobagoConstants.ATTR_TRANSITION;
029    import static org.apache.myfaces.tobago.TobagoConstants.FACET_POPUP;
030    import static org.apache.myfaces.tobago.TobagoConstants.FACET_CONFIRMATION;
031    import org.apache.myfaces.tobago.component.ComponentUtil;
032    import org.apache.myfaces.tobago.component.UIPopup;
033    import org.apache.myfaces.tobago.event.PopupActionListener;
034    import org.apache.myfaces.tobago.context.ClientProperties;
035    
036    import javax.faces.context.FacesContext;
037    import javax.faces.component.UIComponent;
038    import javax.faces.component.ValueHolder;
039    import javax.faces.component.UIParameter;
040    import javax.faces.component.UICommand;
041    import javax.faces.application.Application;
042    import javax.faces.application.ViewHandler;
043    import java.util.List;
044    import java.net.URLDecoder;
045    
046    /**
047     * User: lofwyr
048     * Date: 19.03.2007 17:54:59
049     */
050    public class CommandRendererHelper {
051    
052      private static final Log LOG = LogFactory.getLog(CommandRendererHelper.class);
053    
054      private String onclick;
055      private boolean disabled;
056      private String href;
057      private String target;
058    
059      public CommandRendererHelper(FacesContext facesContext, UICommand component) {
060        initOnclick(facesContext, component, null);
061      }
062    
063      public CommandRendererHelper(FacesContext facesContext, UICommand component, Tag tag) {
064        initOnclick(facesContext, component, tag);
065      }
066    
067      private void initOnclick(FacesContext facesContext, UICommand command, Tag tag) {
068    
069        disabled = ComponentUtil.getBooleanAttribute(command, ATTR_DISABLED);
070        href = getEmptyHref(facesContext);
071    
072        if (disabled) {
073          onclick = "";
074          href = "";
075        } else {
076    
077          UIPopup popup = (UIPopup) command.getFacet(FACET_POPUP);
078          if (popup != null) {
079            if (!ComponentUtil.containsPopupActionListener(command)) {
080              command.addActionListener(new PopupActionListener(popup));
081            }
082          }
083    
084          String clientId = command.getClientId(facesContext);
085          boolean defaultCommand = ComponentUtil.getBooleanAttribute(command, ATTR_DEFAULT_COMMAND);
086          boolean transition = ComponentUtil.getBooleanAttribute(command, ATTR_TRANSITION);
087    
088          if (command.getAttributes().get(ATTR_ACTION_LINK) != null) {
089            String url = generateUrl(facesContext, command);
090            if (tag == Tag.ANCHOR) {
091              onclick = null;
092              href = url;
093              target = ComponentUtil.getStringAttribute(command, ATTR_TARGET);
094            } else {
095              onclick = "Tobago.navigateToUrl('" + url + "');";
096            }
097          } else if (command.getAttributes().get(ATTR_ACTION_ONCLICK) != null) {
098            onclick = prepareOnClick(facesContext, command);
099          } else if (command instanceof org.apache.myfaces.tobago.component.UICommand
100              &&  ((org.apache.myfaces.tobago.component.UICommand) command).getRenderedPartially().length > 0) {
101    
102            String[] componentId = ((org.apache.myfaces.tobago.component.UICommand) command).getRenderedPartially();
103    
104            if (componentId != null && componentId.length == 1) {
105              // TODO find a better way
106              boolean popupAction = ComponentUtil.containsPopupActionListener(command);
107              if (popupAction) {
108                onclick = "Tobago.openPopupWithAction('"
109                    + HtmlRendererUtil.getComponentId(facesContext, command, componentId[0]) + "', '" + clientId + "')";
110              } else {
111                onclick = "Tobago.reloadComponent('"
112                    + HtmlRendererUtil.getComponentId(facesContext, command, componentId[0]) + "','" + clientId + "', {});";
113              }
114            } else {
115              LOG.error("more than one parially rendered component is currently not supported " + componentId);
116              onclick = "Tobago.submitAction('" + clientId + "', " + transition + ");";
117            }
118    
119          } else if (defaultCommand) {
120            ComponentUtil.findPage(facesContext, command).setDefaultActionId(clientId);
121            onclick = null;
122          } else {
123            String target = ComponentUtil.getStringAttribute(command, ATTR_TARGET);
124            if (target == null) {
125              onclick = "Tobago.submitAction('" + clientId + "', " + transition + ");";
126            } else {
127              onclick = "Tobago.submitAction('" + clientId + "', " + transition + ", '" + target + "');";
128            }
129          }
130    
131          if (command.getAttributes().get(ATTR_POPUP_CLOSE) != null
132              && ComponentUtil.isInPopup(command)) {
133            String value = (String) command.getAttributes().get(ATTR_POPUP_CLOSE);
134            if (value.equals("immediate")) {
135              onclick = "Tobago.closePopup(this);";
136            } else if (value.equals("afterSubmit")
137                && command instanceof org.apache.myfaces.tobago.component.UICommand
138                && ((org.apache.myfaces.tobago.component.UICommand) command).getRenderedPartially().length > 0) {
139              onclick += "Tobago.closePopup(this);";
140            }
141    
142          }
143    
144          onclick = appendConfirmationScript(onclick, command);
145        }
146      }
147    
148      private String getEmptyHref(FacesContext facesContext) {
149        ClientProperties clientProperties = ClientProperties.getInstance(facesContext);
150        return clientProperties.getUserAgent().isMsie() ? "#" : "javascript:;";
151      }
152    
153      private String prepareOnClick(FacesContext facesContext, UIComponent component) {
154        String onclick;
155        onclick = (String) component.getAttributes().get(ATTR_ACTION_ONCLICK);
156        if (onclick.contains("@autoId")) {
157          onclick = onclick.replace("@autoId", component.getClientId(facesContext));
158        }
159        return onclick;
160      }
161    
162      private String appendConfirmationScript(String onclick, UIComponent component) {
163        ValueHolder confirmation = (ValueHolder) component.getFacet(FACET_CONFIRMATION);
164        if (confirmation != null) {
165          StringBuilder script = new StringBuilder();
166          script.append("return confirm('");
167          script.append(confirmation.getValue());
168          script.append("')");
169          if (onclick != null) {
170            script.append(" && ");
171            script.append(onclick);
172          }
173          onclick = script.toString();
174        }
175        return onclick;
176      }
177    
178      private String generateUrl(FacesContext facesContext, UIComponent component) {
179        String url;
180        Application application = facesContext.getApplication();
181        ViewHandler viewHandler = application.getViewHandler();
182    
183        String link = (String) component.getAttributes().get(ATTR_ACTION_LINK);
184        if (link.startsWith("/")) { // internal URL
185          url = viewHandler.getActionURL(facesContext, link);
186        } else { // external URL
187          url = link;
188        }
189    
190        url = facesContext.getExternalContext().encodeActionURL(url);
191    
192        StringBuilder builder = new StringBuilder(url);
193        boolean firstParameter = !url.contains("?");
194        for (UIComponent child : (List<UIComponent>) component.getChildren()) {
195          if (child instanceof UIParameter) {
196            UIParameter parameter = (UIParameter) child;
197            if (firstParameter) {
198              builder.append("?");
199              firstParameter = false;
200            } else {
201              builder.append("&");
202            }
203            builder.append(parameter.getName());
204            builder.append("=");
205            Object value = parameter.getValue();
206            // TODO encoding
207            builder.append(value != null ? URLDecoder.decode(value.toString()) : null);
208          }
209        }
210        url = builder.toString();
211    
212        return url;
213      }
214    
215    
216      public String getOnclick() {
217        return onclick;
218      }
219    
220      public String getOnclickDoubleQuoted() {
221        return onclick.replaceAll("'", "\"");
222      }
223    
224      public boolean isDisabled() {
225        return disabled;
226      }
227    
228      public String getHref() {
229        return href;
230      }
231    
232      public String getTarget() {
233        return target;
234      }
235    
236      public static enum Tag {
237        ANCHOR, BUTTON
238      }
239    }