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