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.util.Arrays;
045    import java.net.URLDecoder;
046    
047    /**
048     * User: lofwyr
049     * Date: 19.03.2007 17:54:59
050     */
051    public class CommandRendererHelper {
052    
053      private static final Log LOG = LogFactory.getLog(CommandRendererHelper.class);
054    
055      private String onclick;
056      private boolean disabled;
057      private String href;
058      private String target;
059    
060      public CommandRendererHelper(FacesContext facesContext, UICommand component) {
061        initOnclick(facesContext, component, null);
062      }
063    
064      public CommandRendererHelper(FacesContext facesContext, UICommand component, Tag tag) {
065        initOnclick(facesContext, component, tag);
066      }
067    
068      private void initOnclick(FacesContext facesContext, UICommand command, Tag tag) {
069    
070        disabled = ComponentUtil.getBooleanAttribute(command, ATTR_DISABLED);
071        href = getEmptyHref(facesContext);
072    
073        if (disabled) {
074          onclick = "";
075          href = "";
076        } else {
077    
078          UIPopup popup = (UIPopup) command.getFacet(FACET_POPUP);
079          if (popup != null) {
080            if (!ComponentUtil.containsPopupActionListener(command)) {
081              command.addActionListener(new PopupActionListener(popup));
082            }
083          }
084    
085          String clientId = command.getClientId(facesContext);
086          boolean defaultCommand = ComponentUtil.getBooleanAttribute(command, ATTR_DEFAULT_COMMAND);
087          boolean transition = ComponentUtil.getBooleanAttribute(command, ATTR_TRANSITION);
088    
089          if (command.getAttributes().get(ATTR_ACTION_LINK) != null) {
090            String url = generateUrl(facesContext, command);
091            if (tag == Tag.ANCHOR) {
092              onclick = null;
093              href = url;
094              target = ComponentUtil.getStringAttribute(command, ATTR_TARGET);
095            } else {
096              onclick = "Tobago.navigateToUrl('" + url + "');";
097            }
098          } else if (command.getAttributes().get(ATTR_ACTION_ONCLICK) != null) {
099            onclick = prepareOnClick(facesContext, command);
100          } else if (command instanceof org.apache.myfaces.tobago.component.UICommand
101              && ((org.apache.myfaces.tobago.component.UICommand) command).getRenderedPartially().length > 0) {
102    
103            String[] componentId = ((org.apache.myfaces.tobago.component.UICommand) command).getRenderedPartially();
104    
105            if (componentId != null && componentId.length == 1) {
106              // TODO find a better way
107              boolean popupAction = ComponentUtil.containsPopupActionListener(command);
108              if (popupAction) {
109                onclick = "Tobago.openPopupWithAction('"
110                    + HtmlRendererUtil.getComponentId(facesContext, command, componentId[0]) + "', '" + clientId + "')";
111              } else {
112                onclick = "Tobago.reloadComponent('"
113                    + HtmlRendererUtil.getComponentId(facesContext, command, componentId[0]) + "','" + clientId + "', {});";
114              }
115            } else {
116              LOG.error("more than one parially rendered component is currently not supported "
117                  + Arrays.toString(componentId));
118              onclick = "Tobago.submitAction('" + clientId + "', " + transition + ");";
119            }
120    
121          } else if (defaultCommand) {
122            ComponentUtil.findPage(facesContext, command).setDefaultActionId(clientId);
123            onclick = null;
124          } else {
125            String target = ComponentUtil.getStringAttribute(command, ATTR_TARGET);
126            if (target == null) {
127              onclick = "Tobago.submitAction('" + clientId + "', " + transition + ");";
128            } else {
129              onclick = "Tobago.submitAction('" + clientId + "', " + transition + ", '" + target + "');";
130            }
131          }
132    
133          if (command.getAttributes().get(ATTR_POPUP_CLOSE) != null
134              && ComponentUtil.isInPopup(command)) {
135            String value = (String) command.getAttributes().get(ATTR_POPUP_CLOSE);
136            if (value.equals("immediate")) {
137              onclick = "Tobago.closePopup(this);";
138            } else if (value.equals("afterSubmit")
139                && command instanceof org.apache.myfaces.tobago.component.UICommand
140                && ((org.apache.myfaces.tobago.component.UICommand) command).getRenderedPartially().length > 0) {
141              onclick += "Tobago.closePopup(this);";
142            }
143    
144          }
145    
146          onclick = appendConfirmationScript(onclick, command);
147        }
148      }
149    
150      private String getEmptyHref(FacesContext facesContext) {
151        ClientProperties clientProperties = ClientProperties.getInstance(facesContext);
152        return clientProperties.getUserAgent().isMsie() ? "#" : "javascript:;";
153      }
154    
155      private String prepareOnClick(FacesContext facesContext, UIComponent component) {
156        String onclick;
157        onclick = (String) component.getAttributes().get(ATTR_ACTION_ONCLICK);
158        if (onclick.contains("@autoId")) {
159          onclick = onclick.replace("@autoId", component.getClientId(facesContext));
160        }
161        return onclick;
162      }
163    
164      private String appendConfirmationScript(String onclick, UIComponent component) {
165        ValueHolder confirmation = (ValueHolder) component.getFacet(FACET_CONFIRMATION);
166        if (confirmation != null) {
167          StringBuilder script = new StringBuilder();
168          script.append("return confirm('");
169          script.append(confirmation.getValue());
170          script.append("')");
171          if (onclick != null) {
172            script.append(" && ");
173            script.append(onclick);
174          }
175          onclick = script.toString();
176        }
177        return onclick;
178      }
179    
180      private String generateUrl(FacesContext facesContext, UIComponent component) {
181        String url;
182        Application application = facesContext.getApplication();
183        ViewHandler viewHandler = application.getViewHandler();
184    
185        String link = (String) component.getAttributes().get(ATTR_ACTION_LINK);
186        if (link.startsWith("/")) { // internal URL
187          url = viewHandler.getActionURL(facesContext, link);
188        } else { // external URL
189          url = link;
190        }
191    
192        url = facesContext.getExternalContext().encodeActionURL(url);
193    
194        StringBuilder builder = new StringBuilder(url);
195        boolean firstParameter = !url.contains("?");
196        for (UIComponent child : (List<UIComponent>) component.getChildren()) {
197          if (child instanceof UIParameter) {
198            UIParameter parameter = (UIParameter) child;
199            if (firstParameter) {
200              builder.append("?");
201              firstParameter = false;
202            } else {
203              builder.append("&");
204            }
205            builder.append(parameter.getName());
206            builder.append("=");
207            Object value = parameter.getValue();
208            // TODO encoding
209            builder.append(value != null ? URLDecoder.decode(value.toString()) : null);
210          }
211        }
212        url = builder.toString();
213    
214        return url;
215      }
216    
217    
218      public String getOnclick() {
219        return onclick;
220      }
221    
222      public String getOnclickDoubleQuoted() {
223        return onclick.replaceAll("'", "\"");
224      }
225    
226      public boolean isDisabled() {
227        return disabled;
228      }
229    
230      public String getHref() {
231        return href;
232      }
233    
234      public String getTarget() {
235        return target;
236      }
237    
238      public static enum Tag {
239        ANCHOR, BUTTON
240      }
241    }