Coverage Report - org.apache.tapestry.services.impl.DojoAjaxResponseBuilder
 
Classes in this File Line Coverage Branch Coverage Complexity
DojoAjaxResponseBuilder
0%
0/245
0%
0/142
2.723
 
 1  
 // Copyright May 8, 2006 The Apache Software Foundation
 2  
 //
 3  
 // Licensed under the Apache License, Version 2.0 (the "License");
 4  
 // you may not use this file except in compliance with the License.
 5  
 // You may obtain a copy of the License at
 6  
 //
 7  
 //     http://www.apache.org/licenses/LICENSE-2.0
 8  
 //
 9  
 // Unless required by applicable law or agreed to in writing, software
 10  
 // distributed under the License is distributed on an "AS IS" BASIS,
 11  
 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 12  
 // See the License for the specific language governing permissions and
 13  
 // limitations under the License.
 14  
 package org.apache.tapestry.services.impl;
 15  
 
 16  
 import org.apache.commons.logging.Log;
 17  
 import org.apache.commons.logging.LogFactory;
 18  
 import org.apache.hivemind.Resource;
 19  
 import org.apache.hivemind.util.Defense;
 20  
 import org.apache.tapestry.*;
 21  
 import org.apache.tapestry.asset.AssetFactory;
 22  
 import org.apache.tapestry.engine.IEngineService;
 23  
 import org.apache.tapestry.engine.NullWriter;
 24  
 import org.apache.tapestry.markup.MarkupWriterSource;
 25  
 import org.apache.tapestry.markup.NestedMarkupWriterImpl;
 26  
 import org.apache.tapestry.services.RequestLocaleManager;
 27  
 import org.apache.tapestry.services.ResponseBuilder;
 28  
 import org.apache.tapestry.services.ServiceConstants;
 29  
 import org.apache.tapestry.util.ContentType;
 30  
 import org.apache.tapestry.util.PageRenderSupportImpl;
 31  
 import org.apache.tapestry.util.ScriptUtils;
 32  
 import org.apache.tapestry.web.WebResponse;
 33  
 
 34  
 import java.io.IOException;
 35  
 import java.io.PrintWriter;
 36  
 import java.util.*;
 37  
 
 38  
 
 39  
 /**
 40  
  * Main class that handles dojo based ajax responses. These responses are wrapped
 41  
  * by an xml document format that segments off invididual component/javascript response
 42  
  * types into easy to manage xml elements that can then be interpreted and managed by 
 43  
  * running client-side javascript.
 44  
  *
 45  
  */
 46  
 public class DojoAjaxResponseBuilder implements ResponseBuilder
 47  
 {
 48  0
     private static final Log _log = LogFactory.getLog(DojoAjaxResponseBuilder.class);
 49  
 
 50  0
     private static final String NEWLINE = System.getProperty("line.separator");
 51  
 
 52  
     private final AssetFactory _assetFactory;
 53  
 
 54  
     private final String _namespace;
 55  
 
 56  
     private PageRenderSupportImpl _prs;
 57  
 
 58  
     // used to create IMarkupWriter
 59  
     private RequestLocaleManager _localeManager;
 60  
     private MarkupWriterSource _markupWriterSource;
 61  
     private WebResponse _response;
 62  
 
 63  
     private List _errorPages;
 64  
 
 65  
     private ContentType _contentType;
 66  
 
 67  
     // our response writer
 68  
     private IMarkupWriter _writer;
 69  
     // Parts that will be updated.
 70  0
     private List _parts = new ArrayList();
 71  
     // Map of specialized writers, like scripts
 72  0
     private Map _writers = new HashMap();
 73  
     // List of status messages.
 74  
     private List _statusMessages;
 75  
 
 76  
     private IRequestCycle _cycle;
 77  
 
 78  
     private IEngineService _pageService;
 79  
 
 80  
     /**
 81  
      * Keeps track of renders involving a whole page response, such 
 82  
      * as exception pages or pages activated via {@link IRequestCycle#activate(IPage)}.
 83  
      */
 84  0
     private boolean _pageRender = false;
 85  
 
 86  
     /**
 87  
      * Used to keep track of whether or not the appropriate xml response start
 88  
      * block has been started.
 89  
      */
 90  0
     private boolean _responseStarted = false;
 91  
 
 92  
     /**
 93  
      * Creates a builder with a pre-configured {@link IMarkupWriter}.
 94  
      * Currently only used for testing.
 95  
      *
 96  
      * @param cycle
 97  
      *          The current cycle.
 98  
      * @param writer
 99  
      *          The markup writer to render all "good" content to.
 100  
      * @param parts
 101  
      *          A set of string ids of the components that may have
 102  
      *          their responses rendered.
 103  
      * @param errorPages
 104  
      *          List of page names known to be exception pages.
 105  
      */
 106  
     public DojoAjaxResponseBuilder(IRequestCycle cycle, IMarkupWriter writer, List parts, List errorPages)
 107  0
     {
 108  0
         Defense.notNull(cycle, "cycle");
 109  0
         Defense.notNull(writer, "writer");
 110  
 
 111  0
         _writer = writer;
 112  0
         _cycle = cycle;
 113  
 
 114  0
         if (parts != null)
 115  0
             _parts.addAll(parts);
 116  
 
 117  0
         _namespace = null;
 118  0
         _assetFactory = null;
 119  0
         _errorPages = errorPages;
 120  0
     }
 121  
 
 122  
     /**
 123  
      * Creates a builder with a pre-configured {@link IMarkupWriter}. 
 124  
      * Currently only used for testing.
 125  
      *
 126  
      * @param cycle
 127  
      *          Current request.
 128  
      * @param writer
 129  
      *          The markup writer to render all "good" content to.
 130  
      * @param parts
 131  
      *          A set of string ids of the components that may have 
 132  
      *          their responses rendered.
 133  
      */
 134  
     public DojoAjaxResponseBuilder(IRequestCycle cycle, IMarkupWriter writer, List parts)
 135  
     {
 136  0
         this(cycle, writer, parts, null);
 137  0
     }
 138  
 
 139  
     /**
 140  
      * Creates a new response builder with the required services it needs
 141  
      * to render the response when {@link #renderResponse(IRequestCycle)} is called.
 142  
      *
 143  
      * @param cycle
 144  
      *          The current request.
 145  
      * @param localeManager
 146  
      *          Used to set the locale on the response.
 147  
      * @param markupWriterSource
 148  
      *          Creates IJSONWriter instance to be used.
 149  
      * @param webResponse
 150  
      *          Web response for output stream.
 151  
      * @param errorPages
 152  
      *          List of page names known to be exception pages.
 153  
      * @param assetFactory
 154  
      *          Used to manage asset source inclusions.
 155  
      * @param namespace
 156  
      *          The core namespace to use for javascript/client side operations.
 157  
      * @param pageService
 158  
      *          {@link org.apache.tapestry.engine.PageService} used to generate page urls.
 159  
      */
 160  
     public DojoAjaxResponseBuilder(IRequestCycle cycle,
 161  
                                    RequestLocaleManager localeManager,
 162  
                                    MarkupWriterSource markupWriterSource,
 163  
                                    WebResponse webResponse, List errorPages,
 164  
                                    AssetFactory assetFactory, String namespace, IEngineService pageService)
 165  0
     {
 166  0
         Defense.notNull(cycle, "cycle");
 167  0
         Defense.notNull(assetFactory, "assetService");
 168  
 
 169  0
         _cycle = cycle;
 170  0
         _localeManager = localeManager;
 171  0
         _markupWriterSource = markupWriterSource;
 172  0
         _response = webResponse;
 173  0
         _errorPages = errorPages;
 174  0
         _pageService = pageService;
 175  
 
 176  
         // Used by PageRenderSupport
 177  
 
 178  0
         _assetFactory = assetFactory;
 179  0
         _namespace = namespace;
 180  
 
 181  0
         _prs = new PageRenderSupportImpl(_assetFactory, _namespace, this, cycle);
 182  0
     }
 183  
 
 184  
     /**
 185  
      *
 186  
      * {@inheritDoc}
 187  
      */
 188  
     public boolean isDynamic()
 189  
     {
 190  0
         return true;
 191  
     }
 192  
 
 193  
     /**
 194  
      * {@inheritDoc}
 195  
      */
 196  
     public void renderResponse(IRequestCycle cycle)
 197  
       throws IOException
 198  
     {
 199  
         // if response was already started
 200  
 
 201  0
         if (_responseStarted)
 202  
         {
 203  
             // clear out any previous input
 204  0
             clearPartialWriters();
 205  
 
 206  0
             cycle.renderPage(this);
 207  
 
 208  0
             TapestryUtils.removePageRenderSupport(cycle);
 209  
             
 210  0
             endResponse();
 211  
 
 212  0
             _writer.close();
 213  
 
 214  0
             return;
 215  
         }
 216  
 
 217  0
         _localeManager.persistLocale();
 218  0
         _contentType = new ContentType(CONTENT_TYPE + ";charset=" + cycle.getInfrastructure().getOutputEncoding());
 219  
 
 220  0
         String encoding = _contentType.getParameter(ENCODING_KEY);
 221  
 
 222  0
         if (encoding == null)
 223  
         {
 224  0
             encoding = cycle.getEngine().getOutputEncoding();
 225  
 
 226  0
             _contentType.setParameter(ENCODING_KEY, encoding);
 227  
         }
 228  
 
 229  0
         if (_writer == null)
 230  
         {
 231  0
             parseParameters(cycle);
 232  
 
 233  0
             PrintWriter printWriter = _response.getPrintWriter(_contentType);
 234  0
             _writer = _markupWriterSource.newMarkupWriter(printWriter, _contentType);
 235  
         }
 236  
 
 237  
         // render response
 238  
 
 239  0
         TapestryUtils.storePageRenderSupport(cycle, _prs);
 240  
 
 241  0
         cycle.renderPage(this);
 242  
 
 243  0
         TapestryUtils.removePageRenderSupport(cycle);
 244  
 
 245  0
         endResponse();
 246  
 
 247  0
         _writer.close();
 248  0
     }
 249  
 
 250  
     public void flush()
 251  
       throws IOException
 252  
     {
 253  
         // Important - causes any cookies stored to properly be written out before the
 254  
         // rest of the response starts being written - see TAPESTRY-825
 255  
 
 256  0
         _writer.flush();
 257  
 
 258  0
         if (!_responseStarted)
 259  0
             beginResponse();
 260  0
     }
 261  
 
 262  
     /**
 263  
      * {@inheritDoc}
 264  
      */
 265  
     public void updateComponent(String id)
 266  
     {
 267  0
         if (!_parts.contains(id))
 268  0
             _parts.add(id);
 269  0
     }
 270  
 
 271  
     /**
 272  
      * {@inheritDoc}
 273  
      */
 274  
     public IMarkupWriter getWriter()
 275  
     {
 276  0
         return _writer;
 277  
     }
 278  
 
 279  
     void setWriter(IMarkupWriter writer)
 280  
     {
 281  0
         _writer = writer;
 282  0
     }
 283  
 
 284  
     /**
 285  
      * {@inheritDoc}
 286  
      */
 287  
     public boolean isBodyScriptAllowed(IComponent target)
 288  
     {
 289  0
         if (_pageRender)
 290  0
             return true;
 291  
 
 292  0
         if (target != null
 293  
             && IPage.class.isInstance(target)
 294  
             || (IForm.class.isInstance(target)
 295  
                 && ((IForm)target).isFormFieldUpdating()))
 296  0
             return true;
 297  
 
 298  0
         return contains(target);
 299  
     }
 300  
 
 301  
     /**
 302  
      * {@inheritDoc}
 303  
      */
 304  
     public boolean isExternalScriptAllowed(IComponent target)
 305  
     {
 306  0
         if (_pageRender)
 307  0
             return true;
 308  
 
 309  0
         if (target != null
 310  
             && IPage.class.isInstance(target)
 311  
             || (IForm.class.isInstance(target)
 312  
                 && ((IForm)target).isFormFieldUpdating()))
 313  0
             return true;
 314  
 
 315  0
         return contains(target);
 316  
     }
 317  
 
 318  
     /**
 319  
      * {@inheritDoc}
 320  
      */
 321  
     public boolean isInitializationScriptAllowed(IComponent target)
 322  
     {
 323  0
         if (_log.isDebugEnabled())
 324  
         {
 325  0
             _log.debug("isInitializationScriptAllowed(" + target + ") contains?: " + contains(target) + " _pageRender: " + _pageRender);
 326  
         }
 327  
 
 328  0
         if (_pageRender)
 329  0
             return true;
 330  
 
 331  0
         if (target != null
 332  
             && IPage.class.isInstance(target)
 333  
             || (IForm.class.isInstance(target)
 334  
                 && ((IForm)target).isFormFieldUpdating()))
 335  0
             return true;
 336  
 
 337  0
         return contains(target);
 338  
     }
 339  
 
 340  
     /**
 341  
      * {@inheritDoc}
 342  
      */
 343  
     public boolean isImageInitializationAllowed(IComponent target)
 344  
     {
 345  0
         if (_pageRender)
 346  0
             return true;
 347  
 
 348  0
         if (target != null
 349  
             && IPage.class.isInstance(target)
 350  
             || (IForm.class.isInstance(target)
 351  
                 && ((IForm)target).isFormFieldUpdating()))
 352  0
             return true;
 353  
 
 354  0
         return contains(target);
 355  
     }
 356  
 
 357  
     /**
 358  
      * {@inheritDoc}
 359  
      */
 360  
     public String getPreloadedImageReference(IComponent target, IAsset source)
 361  
     {
 362  0
         return _prs.getPreloadedImageReference(target, source);
 363  
     }
 364  
 
 365  
     /**
 366  
      * {@inheritDoc}
 367  
      */
 368  
     public String getPreloadedImageReference(IComponent target, String url)
 369  
     {
 370  0
         return _prs.getPreloadedImageReference(target, url);
 371  
     }
 372  
 
 373  
     /**
 374  
      * {@inheritDoc}
 375  
      */
 376  
     public String getPreloadedImageReference(String url)
 377  
     {
 378  0
         return _prs.getPreloadedImageReference(url);
 379  
     }
 380  
 
 381  
     /**
 382  
      * {@inheritDoc}
 383  
      */
 384  
     public void addBodyScript(IComponent target, String script)
 385  
     {
 386  0
         _prs.addBodyScript(target, script);
 387  0
     }
 388  
 
 389  
     /**
 390  
      * {@inheritDoc}
 391  
      */
 392  
     public void addBodyScript(String script)
 393  
     {
 394  0
         _prs.addBodyScript(script);
 395  0
     }
 396  
 
 397  
     /**
 398  
      * {@inheritDoc}
 399  
      */
 400  
     public void addExternalScript(IComponent target, Resource resource)
 401  
     {
 402  0
         _prs.addExternalScript(target, resource);
 403  0
     }
 404  
 
 405  
     /**
 406  
      * {@inheritDoc}
 407  
      */
 408  
     public void addExternalScript(Resource resource)
 409  
     {
 410  0
         _prs.addExternalScript(resource);
 411  0
     }
 412  
 
 413  
     /**
 414  
      * {@inheritDoc}
 415  
      */
 416  
     public void addInitializationScript(IComponent target, String script)
 417  
     {
 418  0
         _prs.addInitializationScript(target, script);
 419  0
     }
 420  
 
 421  
     /**
 422  
      * {@inheritDoc}
 423  
      */
 424  
     public void addInitializationScript(String script)
 425  
     {
 426  0
         _prs.addInitializationScript(script);
 427  0
     }
 428  
 
 429  
     public void addScriptAfterInitialization(IComponent target, String script)
 430  
     {
 431  0
         _prs.addScriptAfterInitialization(target, script);
 432  0
     }
 433  
 
 434  
     /**
 435  
      * {@inheritDoc}
 436  
      */
 437  
     public String getUniqueString(String baseValue)
 438  
     {
 439  0
         return _prs.getUniqueString(baseValue);
 440  
     }
 441  
 
 442  
     /**
 443  
      * {@inheritDoc}
 444  
      */
 445  
     public void writeBodyScript(IMarkupWriter writer, IRequestCycle cycle)
 446  
     {
 447  0
         _prs.writeBodyScript(writer, cycle);
 448  0
     }
 449  
 
 450  
     /**
 451  
      * {@inheritDoc}
 452  
      */
 453  
     public void writeInitializationScript(IMarkupWriter writer)
 454  
     {
 455  0
         _prs.writeInitializationScript(writer);
 456  0
     }
 457  
 
 458  
     /**
 459  
      * {@inheritDoc}
 460  
      */
 461  
     public void beginBodyScript(IMarkupWriter normalWriter, IRequestCycle cycle)
 462  
     {
 463  0
         IMarkupWriter writer = getWriter(ResponseBuilder.BODY_SCRIPT, ResponseBuilder.SCRIPT_TYPE);
 464  
 
 465  0
         writer.begin("script");
 466  0
         writer.printRaw(NEWLINE + "//<![CDATA[" + NEWLINE);
 467  0
     }
 468  
 
 469  
     /**
 470  
      * {@inheritDoc}
 471  
      */
 472  
     public void endBodyScript(IMarkupWriter normalWriter, IRequestCycle cycle)
 473  
     {
 474  0
         IMarkupWriter writer = getWriter(ResponseBuilder.BODY_SCRIPT, ResponseBuilder.SCRIPT_TYPE);
 475  
 
 476  0
         writer.printRaw(NEWLINE + "//]]>" + NEWLINE);
 477  0
         writer.end();
 478  0
     }
 479  
 
 480  
     /**
 481  
      * {@inheritDoc}
 482  
      */
 483  
     public void writeBodyScript(IMarkupWriter normalWriter, String script, IRequestCycle cycle)
 484  
     {
 485  0
         IMarkupWriter writer = getWriter(ResponseBuilder.BODY_SCRIPT, ResponseBuilder.SCRIPT_TYPE);
 486  
 
 487  0
         writer.printRaw(script);
 488  0
     }
 489  
 
 490  
     /**
 491  
      * {@inheritDoc}
 492  
      */
 493  
     public void writeExternalScript(IMarkupWriter normalWriter, String url, IRequestCycle cycle)
 494  
     {
 495  0
         IMarkupWriter writer = getWriter(ResponseBuilder.INCLUDE_SCRIPT, ResponseBuilder.SCRIPT_TYPE);
 496  
 
 497  
         // causes asset includes to be loaded dynamically into document head
 498  0
         writer.beginEmpty("include");
 499  0
         writer.attribute("url", url);
 500  0
     }
 501  
 
 502  
     /**
 503  
      * {@inheritDoc}
 504  
      */
 505  
     public void writeImageInitializations(IMarkupWriter normalWriter, String script, String preloadName, IRequestCycle cycle)
 506  
     {
 507  0
         IMarkupWriter writer = getWriter(ResponseBuilder.BODY_SCRIPT, ResponseBuilder.SCRIPT_TYPE);
 508  
 
 509  0
         writer.printRaw(NEWLINE + preloadName + " = [];" + NEWLINE);
 510  0
         writer.printRaw("if (document.images) {" + NEWLINE);
 511  
 
 512  0
         writer.printRaw(script);
 513  
 
 514  0
         writer.printRaw("}" + NEWLINE);
 515  0
     }
 516  
 
 517  
     /**
 518  
      * {@inheritDoc}
 519  
      */
 520  
     public void writeInitializationScript(IMarkupWriter normalWriter, String script)
 521  
     {
 522  0
         IMarkupWriter writer = getWriter(ResponseBuilder.INITIALIZATION_SCRIPT, ResponseBuilder.SCRIPT_TYPE);
 523  
 
 524  0
         writer.begin("script");
 525  
 
 526  
         // return is in XML so must escape any potentially non-xml compliant content
 527  0
         writer.printRaw(NEWLINE + "//<![CDATA[" + NEWLINE);
 528  
 
 529  0
         writer.printRaw(script);
 530  
 
 531  0
         writer.printRaw(NEWLINE + "//]]>" + NEWLINE);
 532  
 
 533  0
         writer.end();
 534  0
     }
 535  
 
 536  
     public void addStatus(IMarkupWriter normalWriter, String text)
 537  
     {
 538  0
         addStatusMessage(normalWriter, "info", text);
 539  0
     }
 540  
 
 541  
     /**
 542  
      * Adds a status message to the current response. This implementation keeps track
 543  
      * of all messages and appends them to the XHR response. On the client side, 
 544  
      * the default behavior is to publish the message to a topic matching the category name
 545  
      * using <code>dojo.event.topic.publish(category,text);</code>.
 546  
      *
 547  
      * @param normalWriter
 548  
      *          The markup writer to use, this may be ignored or swapped
 549  
      *          out for a different writer depending on the implementation being used.
 550  
      * @param category
 551  
      *          Allows setting a category that best describes the type of the status message,
 552  
      *          i.e. info, error, e.t.c.
 553  
      * @param text
 554  
      *          The status message. 
 555  
      */
 556  
     public void addStatusMessage(IMarkupWriter normalWriter, String category, String text)
 557  
     {
 558  0
         if (_statusMessages==null)
 559  
         {
 560  0
             _statusMessages = new ArrayList();
 561  
         }
 562  
 
 563  0
         _statusMessages.add(category);
 564  0
         _statusMessages.add(text);
 565  0
     }
 566  
 
 567  
     void writeStatusMessages() {
 568  
 
 569  0
         for (int i=0; i < _statusMessages.size(); i+=2)
 570  
         {
 571  0
             IMarkupWriter writer = getWriter((String) _statusMessages.get(i), "status");
 572  
 
 573  0
             writer.printRaw((String) _statusMessages.get(i+1));
 574  
         }
 575  
 
 576  0
         _statusMessages = null;
 577  0
     }
 578  
 
 579  
     /**
 580  
      * {@inheritDoc}
 581  
      */
 582  
     public void render(IMarkupWriter writer, IRender render, IRequestCycle cycle)
 583  
     {
 584  
         // must be a valid writer already
 585  
 
 586  0
         if (NestedMarkupWriterImpl.class.isInstance(writer) && !NullWriter.class.isInstance(writer))
 587  
         {
 588  0
             render.render(writer, cycle);
 589  0
             return;
 590  
         }
 591  
 
 592  
         // check for page exception renders and write content to writer so client can display them
 593  
 
 594  0
         if (IPage.class.isInstance(render))
 595  
         {
 596  0
             IPage page = (IPage)render;
 597  0
             String errorPage = getErrorPage(page.getPageName());
 598  
 
 599  0
             if (errorPage != null)
 600  
             {
 601  0
                 _pageRender = true;
 602  
 
 603  0
                 clearPartialWriters();
 604  0
                 render.render(getWriter(errorPage, EXCEPTION_TYPE), cycle);
 605  0
                 return;
 606  
             }
 607  
 
 608  
             // If a page other than the active page originally requested is rendered
 609  
             // it means someone activated a new page, so we need to tell the client to handle
 610  
             // this appropriately. (usually by replacing the current dom with whatever this renders)
 611  
 
 612  0
             if (_cycle.getParameter(ServiceConstants.PAGE) != null
 613  
                 && !page.getPageName().equals(_cycle.getParameter(ServiceConstants.PAGE)))
 614  
             {
 615  0
                 IMarkupWriter urlwriter = _writer.getNestedWriter();
 616  
 
 617  0
                 urlwriter.begin("response");
 618  0
                 urlwriter.attribute("type", PAGE_TYPE);
 619  0
                 urlwriter.attribute("url", _pageService.getLink(true, page.getPageName()).getAbsoluteURL());
 620  
 
 621  0
                 _writers.put(PAGE_TYPE, urlwriter);
 622  0
                 return;
 623  
             }
 624  
         }
 625  
 
 626  0
         if (IComponent.class.isInstance(render)
 627  
             && contains((IComponent)render, ((IComponent)render).peekClientId()))
 628  
         {
 629  0
             render.render(getComponentWriter( ((IComponent)render).peekClientId() ), cycle);
 630  0
             return;
 631  
         }
 632  
 
 633  
         // Nothing else found, throw out response
 634  
 
 635  0
         render.render(NullWriter.getSharedInstance(), cycle);
 636  0
     }
 637  
 
 638  
     private String getErrorPage(String pageName)
 639  
     {
 640  0
         for (int i=0; i < _errorPages.size(); i++)
 641  
         {
 642  0
             String page = (String)_errorPages.get(i);
 643  
 
 644  0
             if (pageName.indexOf(page) > -1)
 645  0
                 return page;
 646  
         }
 647  
 
 648  0
         return null;
 649  
     }
 650  
 
 651  
     IMarkupWriter getComponentWriter(String id)
 652  
     {
 653  0
         return getWriter(id, ELEMENT_TYPE);
 654  
     }
 655  
 
 656  
     /**
 657  
      *
 658  
      * {@inheritDoc}
 659  
      */
 660  
     public IMarkupWriter getWriter(String id, String type)
 661  
     {
 662  0
         Defense.notNull(id, "id can't be null");
 663  
 
 664  0
         if (!_responseStarted)
 665  0
             beginResponse();
 666  
 
 667  0
         IMarkupWriter w = (IMarkupWriter)_writers.get(id);
 668  0
         if (w != null)
 669  0
             return w;
 670  
 
 671  
         // Make component write to a "nested" writer
 672  
         // so that element begin/ends don't conflict
 673  
         // with xml element response begin/ends. This is very
 674  
         // important.
 675  
 
 676  0
         IMarkupWriter nestedWriter = _writer.getNestedWriter();
 677  0
         nestedWriter.begin("response");
 678  0
         nestedWriter.attribute("id", id);
 679  0
         if (type != null)
 680  0
             nestedWriter.attribute("type", type);
 681  
 
 682  0
         _writers.put(id, nestedWriter);
 683  
 
 684  0
         return nestedWriter;
 685  
     }
 686  
 
 687  
     /**
 688  
      * Called to start an ajax response. Writes xml doctype and starts
 689  
      * the <code>ajax-response</code> element that will contain all of
 690  
      * the returned content.
 691  
      */
 692  
     void beginResponse()
 693  
     {
 694  0
         _responseStarted = true;
 695  
 
 696  0
         _writer.printRaw("<?xml version=\"1.0\" encoding=\"" + _cycle.getInfrastructure().getOutputEncoding() + "\"?>");
 697  0
         _writer.printRaw("<!DOCTYPE html "
 698  
                          + "PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" "
 699  
                          + "\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\" [" + NEWLINE
 700  
                          + "<!ENTITY nbsp '&#160;'>" + NEWLINE
 701  
                          + "]>" + NEWLINE);
 702  
         
 703  0
         _writer.printRaw("<ajax-response>");
 704  0
     }
 705  
 
 706  
     /**
 707  
      * Invoked to clear out tempoary partial writer buffers before rendering exception
 708  
      * page.
 709  
      */
 710  
     void clearPartialWriters()
 711  
     {
 712  0
         _writers.clear();
 713  0
     }
 714  
 
 715  
     /**
 716  
      * Called after the entire response has been captured. Causes
 717  
      * the writer buffer output captured to be segmented and written
 718  
      * out to the right response elements for the client libraries to parse.
 719  
      */
 720  
     void endResponse()
 721  
     {
 722  0
         if (!_responseStarted)
 723  
         {
 724  0
             beginResponse();
 725  
         }
 726  
 
 727  
         // write out captured content
 728  
 
 729  0
         if (_statusMessages != null)
 730  0
             writeStatusMessages();
 731  
 
 732  0
         Iterator keys = _writers.keySet().iterator();
 733  
         String buffer;
 734  
 
 735  0
         while (keys.hasNext())
 736  
         {
 737  0
             String key = (String)keys.next();
 738  0
             NestedMarkupWriter nw = (NestedMarkupWriter)_writers.get(key);
 739  
 
 740  0
             buffer = nw.getBuffer();
 741  
 
 742  0
             if (_log.isDebugEnabled())
 743  
             {
 744  0
                 _log.debug("Ajax markup buffer for key <" + key + " contains: " + buffer);
 745  
             }
 746  
 
 747  0
             if (!isScriptWriter(key))
 748  0
                 _writer.printRaw(ScriptUtils.ensureValidScriptTags(buffer));
 749  
             else
 750  0
                 _writer.printRaw(buffer);
 751  0
         }
 752  
 
 753  
         // end response
 754  
 
 755  0
         _writer.printRaw("</ajax-response>");
 756  0
         _writer.flush();
 757  0
     }
 758  
 
 759  
     /**
 760  
      * Determines if the specified markup writer key is one of
 761  
      * the pre-defined script keys from ResponseBuilder.
 762  
      *
 763  
      * @param key
 764  
      *          The key to check.
 765  
      * @return True, if key is one of the ResponseBuilder keys. 
 766  
      *         (BODY_SCRIPT,INCLUDE_SCRIPT,INITIALIZATION_SCRIPT)
 767  
      */
 768  
     boolean isScriptWriter(String key)
 769  
     {
 770  0
         if (key == null)
 771  0
             return false;
 772  
 
 773  0
         if (ResponseBuilder.BODY_SCRIPT.equals(key)
 774  
             || ResponseBuilder.INCLUDE_SCRIPT.equals(key)
 775  
             || ResponseBuilder.INITIALIZATION_SCRIPT.equals(key))
 776  0
             return true;
 777  
 
 778  0
         return false;
 779  
     }
 780  
 
 781  
     /**
 782  
      * Grabs the incoming parameters needed for json responses, most notable the
 783  
      * {@link ServiceConstants#UPDATE_PARTS} parameter.
 784  
      *
 785  
      * @param cycle
 786  
      *            The request cycle to parse from
 787  
      */
 788  
     void parseParameters(IRequestCycle cycle)
 789  
     {
 790  0
         Object[] updateParts = cycle.getParameters(ServiceConstants.UPDATE_PARTS);
 791  
 
 792  0
         if (updateParts == null)
 793  0
             return;
 794  
 
 795  0
         for(int i = 0; i < updateParts.length; i++)
 796  0
             _parts.add(updateParts[i].toString());
 797  0
     }
 798  
 
 799  
     /**
 800  
      * Determines if the specified component is contained in the 
 801  
      * responses requested update parts.
 802  
      * @param target
 803  
      *          The component to check for.
 804  
      * @return True if the request should capture the components output.
 805  
      */
 806  
     public boolean contains(IComponent target)
 807  
     {
 808  0
         if (target == null)
 809  0
             return false;
 810  
 
 811  0
         String id = target.getClientId();
 812  
 
 813  0
         return contains(target, id);
 814  
     }
 815  
 
 816  
     boolean contains(IComponent target, String id)
 817  
     {
 818  0
         if (_parts.contains(id))
 819  0
             return true;
 820  
 
 821  0
         Iterator it = _cycle.renderStackIterator();
 822  0
         while (it.hasNext())
 823  
         {
 824  0
             IComponent comp = (IComponent)it.next();
 825  0
             String compId = comp.getClientId();
 826  
 
 827  0
             if (comp != target && _parts.contains(compId))
 828  0
                 return true;
 829  0
         }
 830  
 
 831  0
         return false;
 832  
     }
 833  
 
 834  
     /**
 835  
      * {@inheritDoc}
 836  
      */
 837  
     public boolean explicitlyContains(IComponent target)
 838  
     {
 839  0
         if (target == null)
 840  0
             return false;
 841  
 
 842  0
         return _parts.contains(target.getId());
 843  
     }
 844  
 }