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