001    package org.apache.myfaces.tobago.ajax.api;
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 org.apache.myfaces.tobago.component.ComponentUtil;
023    import org.apache.myfaces.tobago.component.UIViewRoot;
024    import org.apache.myfaces.tobago.util.ResponseUtils;
025    
026    import javax.faces.FactoryFinder;
027    import javax.faces.application.Application;
028    import javax.faces.application.ViewHandler;
029    import javax.faces.component.UIComponent;
030    import javax.faces.context.FacesContext;
031    import javax.faces.context.ResponseWriter;
032    import javax.faces.event.PhaseId;
033    import javax.faces.render.RenderKit;
034    import javax.faces.render.RenderKitFactory;
035    import javax.faces.render.Renderer;
036    import javax.servlet.http.HttpServletResponse;
037    import java.io.IOException;
038    import java.io.PrintWriter;
039    import java.util.Collections;
040    import java.util.HashMap;
041    import java.util.HashSet;
042    import java.util.Iterator;
043    import java.util.Map;
044    import java.util.Set;
045    import java.util.StringTokenizer;
046    
047    public class AjaxUtils {
048    
049      private static final Log LOG = LogFactory.getLog(AjaxUtils.class);
050    
051      public static final String AJAX_COMPONENTS = AjaxUtils.class.getName() + ".AJAX_COMPONENTS";
052    
053      public static boolean isAjaxRequest(FacesContext facesContext) {
054        Map parameterMap = facesContext.getExternalContext().getRequestParameterMap();
055        String ajaxComponentIds = (String) parameterMap.get(AjaxPhaseListener.AJAX_COMPONENT_ID);
056        return ajaxComponentIds != null;
057      }
058    
059      public static void checkParamValidity(FacesContext facesContext, UIComponent uiComponent, Class compClass) {
060        if (facesContext == null) {
061          throw new NullPointerException("facesContext may not be null");
062        }
063        if (uiComponent == null) {
064          throw new NullPointerException("uiComponent may not be null");
065        }
066        //if (compClass != null && !(compClass.isAssignableFrom(uiComponent.getClass())))
067        // why isAssignableFrom with additional getClass method call if isInstance does the same?
068        if (compClass != null && !(compClass.isInstance(uiComponent))) {
069          throw new IllegalArgumentException("uiComponent : "
070              + uiComponent.getClass().getName() + " is not instance of "
071              + compClass.getName() + " as it should be");
072        }
073      }
074    
075      public static void encodeAjaxComponent(FacesContext facesContext, UIComponent component) throws IOException {
076        if (facesContext == null) {
077          throw new NullPointerException("facesContext");
078        }
079        if (!component.isRendered()) {
080          return;
081        }
082        Renderer renderer = ComponentUtil.getRenderer(facesContext, component);
083        if (renderer != null && renderer instanceof AjaxRenderer) {
084          ((AjaxRenderer) renderer).encodeAjax(facesContext, component);
085        }
086      }
087    
088      public static void processAjax(FacesContext facesContext, UIComponent component)
089          throws IOException {
090        if (component instanceof AjaxComponent) {
091          ((AjaxComponent) component).processAjax(facesContext);
092        } else {
093          processAjaxOnChildren(facesContext, component);
094        }
095      }
096    
097      public static void processActiveAjaxComponent(FacesContext facesContext,
098          UIComponent component)
099          throws IOException {
100    
101        if (component instanceof AjaxComponent) {
102          final UIViewRoot viewRoot = (UIViewRoot) facesContext.getViewRoot();
103    
104          // TODO: handle phaseListeners ??
105    
106          if (!facesContext.getRenderResponse()) {
107            component.processValidators(facesContext);
108            viewRoot.broadcastEventsForPhase(facesContext, PhaseId.PROCESS_VALIDATIONS);
109          } else if (LOG.isDebugEnabled()) {
110            LOG.debug("Skipping validate");
111          }
112    
113          if (!facesContext.getRenderResponse()) {
114            component.processUpdates(facesContext);
115            viewRoot.broadcastEventsForPhase(facesContext, PhaseId.UPDATE_MODEL_VALUES);
116          } else if (LOG.isDebugEnabled()) {
117            LOG.debug("Skipping updates");
118          }
119    
120          if (!facesContext.getRenderResponse()) {
121            viewRoot.processApplication(facesContext);
122          } else if (LOG.isDebugEnabled()) {
123            LOG.debug("Skipping application");
124          }
125    
126          ((AjaxComponent) component).encodeAjax(facesContext);
127        } else {
128          LOG.error("Can't process non AjaxComponent : \""
129              + component.getClientId(facesContext) + "\" = "
130              + component.getClass().getName());
131        }
132      }
133    
134      public static void processAjaxOnChildren(FacesContext facesContext,
135          UIComponent component) throws IOException {
136    
137        final Iterator<UIComponent> facetsAndChildren = component.getFacetsAndChildren();
138        while (facetsAndChildren.hasNext() && !facesContext.getResponseComplete()) {
139          AjaxUtils.processAjax(facesContext, facetsAndChildren.next());
140        }
141      }
142    
143      public static Set<String> getRequestPartialIds(FacesContext facesContext) {
144        Map parameterMap = facesContext.getExternalContext().getRequestParameterMap();
145        String ajaxComponentIds = (String) parameterMap.get(AjaxPhaseListener.AJAX_COMPONENT_ID);
146        if (ajaxComponentIds != null) {
147          StringTokenizer tokenizer = new StringTokenizer(ajaxComponentIds, ",");
148          Set<String> ajaxComponents = new HashSet<String>(tokenizer.countTokens());
149          while (tokenizer.hasMoreTokens()) {
150            String ajaxId = tokenizer.nextToken();
151            ajaxComponents.add(ajaxId);
152          }
153          return ajaxComponents;
154        }
155        return Collections.EMPTY_SET;
156      }
157    
158      public static Map<String, UIComponent> parseAndStoreComponents(FacesContext facesContext) {
159        Map parameterMap = facesContext.getExternalContext().getRequestParameterMap();
160        String ajaxComponentIds = (String) parameterMap.get(AjaxPhaseListener.AJAX_COMPONENT_ID);
161        if (ajaxComponentIds != null) {
162          if (LOG.isDebugEnabled()) {
163            LOG.debug("ajaxComponentIds = \"" + ajaxComponentIds + "\"");
164          }
165          StringTokenizer tokenizer = new StringTokenizer(ajaxComponentIds, ",");
166          Map<String, UIComponent> ajaxComponents = new HashMap<String, UIComponent>(tokenizer.countTokens());
167          //noinspection unchecked
168          facesContext.getExternalContext().getRequestMap().put(AJAX_COMPONENTS, ajaxComponents);
169          javax.faces.component.UIViewRoot viewRoot = facesContext.getViewRoot();
170          while (tokenizer.hasMoreTokens()) {
171            String ajaxId = tokenizer.nextToken();
172            UIComponent ajaxComponent = viewRoot.findComponent(ajaxId);
173            if (ajaxComponent != null) {
174              if (LOG.isDebugEnabled()) {
175                LOG.debug("ajaxComponent for \"" + ajaxId + "\" = \"" + ajaxComponent + "\"");
176              }
177              ajaxComponents.put(ajaxId, ajaxComponent);
178            }
179          }
180          return ajaxComponents;
181        }
182        return null;
183      }
184    
185      public static Map<String, UIComponent> getAjaxComponents(FacesContext facesContext) {
186        //noinspection unchecked
187        return (Map<String, UIComponent>)
188            facesContext.getExternalContext().getRequestMap().get(AJAX_COMPONENTS);
189      }
190    
191      public static void ensureDecoded(FacesContext facesContext, String clientId) {
192        ensureDecoded(facesContext, facesContext.getViewRoot().findComponent(clientId));
193      }
194    
195      public static void ensureDecoded(FacesContext facesContext, UIComponent component) {
196        if (component == null) {
197          LOG.warn("Ignore AjaxComponent: null");
198          return;
199        }
200        Map<String, UIComponent> ajaxComponents = getAjaxComponents(facesContext);
201        if (ajaxComponents != null) {
202          for (UIComponent uiComponent : ajaxComponents.values()) {
203            // is component or a parent of it in the list?
204            UIComponent parent = component;
205            while (parent != null) {
206              if (component == uiComponent) {
207                // nothing to do, because it was already decoded (in the list)
208                return;
209              }
210              parent = parent.getParent();
211            }
212          }
213          component.processDecodes(facesContext);
214        }
215      }
216    
217      public static boolean redirect(FacesContext facesContext, String url) throws IOException {
218        if (!isAjaxRequest(facesContext)) {
219          return false;
220        }
221        ResponseWriter writer = facesContext.getResponseWriter();
222        if (writer == null) {
223          RenderKit renderKit = facesContext.getRenderKit();
224          if (renderKit == null) {
225            RenderKitFactory renderFactory = (RenderKitFactory) FactoryFinder.getFactory(FactoryFinder.RENDER_KIT_FACTORY);
226            Application application = facesContext.getApplication();
227            ViewHandler applicationViewHandler = application.getViewHandler();
228            String renderKitId = applicationViewHandler.calculateRenderKitId(facesContext);
229            renderKit = renderFactory.getRenderKit(facesContext, renderKitId);
230          }
231          writer = renderKit.createResponseWriter(((HttpServletResponse)
232                  facesContext.getExternalContext().getResponse()).getWriter(), null, null);
233        }
234        ResponseUtils.ensureNoCacheHeader(facesContext);
235        writer.startElement("redirect", null);
236        writer.writeAttribute("url", url, null);
237        writer.endElement("redirect");
238        writer.flush();
239        facesContext.responseComplete();
240        return true;
241      }
242    
243      public static void redirect(HttpServletResponse response, String url) throws IOException {
244        PrintWriter out = response.getWriter();
245        out.print("<redirect url=");
246        out.print(url);
247        out.println("</redirect>");
248      }
249    }