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