You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tapestry.apache.org by hl...@apache.org on 2005/03/29 15:35:43 UTC

cvs commit: jakarta-tapestry/framework/src/java/org/apache/tapestry/util PageRenderSupportImpl.java

hlship      2005/03/29 05:35:42

  Modified:    framework/src/java/org/apache/tapestry IRequestCycle.java
                        TapestryMessages.java TapestryStrings2.properties
                        IScript.java IScriptProcessor.java
               framework/src/java/org/apache/tapestry/html Body.jwc
                        Rollover.java Body.java Script.java
               contrib/src/java/org/apache/tapestry/contrib/inspector
                        InspectorButton.java
               framework/src/java/org/apache/tapestry/asset
                        ClasspathAssetFactory.java PrivateAsset.java
               framework/src/java/org/apache/tapestry/valid
                        BaseValidator.java ValidField.java
               eclipse  Tapestry-Workbench.launch
               framework/src/scripts TestLinkRenderers.xml
                        TestValidFieldNoBody.xml
               framework/src/java/org/apache/tapestry/link
                        AbstractLinkComponent.java
               framework/src/descriptor/META-INF tapestry.asset.xml
               contrib/src/java/org/apache/tapestry/contrib/palette
                        Palette.java
               framework/src/java/org/apache/tapestry/form DatePicker.java
                        Form.java LinkSubmit.java FormStrings.properties
                        FormMessages.java
  Added:       framework/src/java/org/apache/tapestry
                        PageRenderSupport.java TapestryUtils.java
               framework/src/test/org/apache/tapestry
                        TestTapestryUtils.java
               framework/src/test/org/apache/tapestry/util
                        TestPageRenderSupport.java
               framework/src/java/org/apache/tapestry/util
                        PageRenderSupportImpl.java
  Log:
  Factor out much of the behavior of the Body component as a new interface (PageRenderSupport) and implementation, so that it can be reused seperately.
  
  Revision  Changes    Path
  1.14      +2 -1      jakarta-tapestry/framework/src/java/org/apache/tapestry/IRequestCycle.java
  
  Index: IRequestCycle.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tapestry/framework/src/java/org/apache/tapestry/IRequestCycle.java,v
  retrieving revision 1.13
  retrieving revision 1.14
  diff -u -r1.13 -r1.14
  --- IRequestCycle.java	27 Mar 2005 17:42:23 -0000	1.13
  +++ IRequestCycle.java	29 Mar 2005 13:35:42 -0000	1.14
  @@ -105,7 +105,8 @@
        * request cycle, that page is returned. Otherwise, the engine's page loader is used to load the
        * page.
        * 
  -     * @throws PageNotFoundException if the page does not exist.
  +     * @throws PageNotFoundException
  +     *             if the page does not exist.
        * @see org.apache.tapestry.engine.IPageSource#getPage(IRequestCycle, String, IMonitor)
        */
   
  
  
  
  1.9       +13 -3     jakarta-tapestry/framework/src/java/org/apache/tapestry/TapestryMessages.java
  
  Index: TapestryMessages.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tapestry/framework/src/java/org/apache/tapestry/TapestryMessages.java,v
  retrieving revision 1.8
  retrieving revision 1.9
  diff -u -r1.8 -r1.9
  --- TapestryMessages.java	24 Mar 2005 16:50:15 -0000	1.8
  +++ TapestryMessages.java	29 Mar 2005 13:35:42 -0000	1.9
  @@ -25,19 +25,29 @@
       private static final MessageFormatter _formatter = new MessageFormatter(TapestryMessages.class,
               "TapestryStrings2");
   
  -    public static String servletInitFailure(Throwable cause)
  +    static String servletInitFailure(Throwable cause)
       {
           return _formatter.format("servlet-init-failure", cause);
       }
   
  -    public static String componentIsLocked(IComponent component)
  +    static String componentIsLocked(IComponent component)
       {
           return _formatter.format("component-is-locked", component.getExtendedId());
       }
   
  -    public static String servletInit(String name, long elapsedToRegistry, long elapsedOverall)
  +    static String servletInit(String name, long elapsedToRegistry, long elapsedOverall)
       {
           return _formatter.format("servlet-init", name, new Long(elapsedToRegistry), new Long(
                   elapsedOverall));
       }
  +
  +    static String nonUniqueAttribute(Object newInstance, String key, Object existingInstance)
  +    {
  +        return _formatter.format("non-unique-attribute", newInstance, key, existingInstance);
  +    }
  +
  +    public static String noPageRenderSupport()
  +    {
  +        return _formatter.getMessage("no-page-render-support");
  +    }
   }
  \ No newline at end of file
  
  
  
  1.8       +2 -0      jakarta-tapestry/framework/src/java/org/apache/tapestry/TapestryStrings2.properties
  
  Index: TapestryStrings2.properties
  ===================================================================
  RCS file: /home/cvs/jakarta-tapestry/framework/src/java/org/apache/tapestry/TapestryStrings2.properties,v
  retrieving revision 1.7
  retrieving revision 1.8
  diff -u -r1.7 -r1.8
  --- TapestryStrings2.properties	24 Mar 2005 16:50:15 -0000	1.7
  +++ TapestryStrings2.properties	29 Mar 2005 13:35:42 -0000	1.8
  @@ -16,3 +16,5 @@
   servlet-init-failure=Unable to initialize application servlet: {0}
   no-accessor=Component {0} does not have accessor methods for property {1}.
   component-is-locked=Component {0} is active and its configuration state may not be changed.
  +non-unique-attribute=Unable to store {0} into request cycle as attribute ''{1}'' because existing object {2} has already been stored. This usually indicates that components are improperly nested.
  +no-page-render-support=No PageRenderSupport object has been stored into the request cycle. This object is typically provided by a Body component. You should add a Body component to your template.
  \ No newline at end of file
  
  
  
  1.4       +1 -1      jakarta-tapestry/framework/src/java/org/apache/tapestry/IScript.java
  
  Index: IScript.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tapestry/framework/src/java/org/apache/tapestry/IScript.java,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -r1.3 -r1.4
  --- IScript.java	6 Jan 2005 02:17:12 -0000	1.3
  +++ IScript.java	29 Mar 2005 13:35:42 -0000	1.4
  @@ -48,7 +48,7 @@
        * @param symbols Map of input symbols; execution of the script may modify the map,
        * creating new output symbols
        * 
  -     * @see org.apache.tapestry.html.Body#get(IRequestCycle)
  +     * @see TapestryUtils#getPageRenderSupport(IRequestCycle, Object)
        *
        */
   
  
  
  
  1.4       +32 -34    jakarta-tapestry/framework/src/java/org/apache/tapestry/IScriptProcessor.java
  
  Index: IScriptProcessor.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tapestry/framework/src/java/org/apache/tapestry/IScriptProcessor.java,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -r1.3 -r1.4
  --- IScriptProcessor.java	6 Jan 2005 02:17:12 -0000	1.3
  +++ IScriptProcessor.java	29 Mar 2005 13:35:42 -0000	1.4
  @@ -17,9 +17,8 @@
   import org.apache.hivemind.Resource;
   
   /**
  - * Defines methods needed by a {@link org.apache.tapestry.IScript} to
  - * execute.
  - *
  + * Defines methods needed by a {@link org.apache.tapestry.IScript}to execute.
  + * 
    * @author Howard Lewis Ship
    * @since 3.0
    * @see org.apache.tapestry.html.Body
  @@ -27,34 +26,33 @@
   
   public interface IScriptProcessor
   {
  -	/**
  -	 * Adds scripting code to the main body.  During the render, multiple scripts may
  -	 * render multiple bodies; all are concatinated together to form
  -	 * a single block.
  -	 */
  -	
  -	public void addBodyScript(String script);
  -	
  -	/**
  -	 * Adds initialization script.  Initialization script is executed once, when
  -	 * the containing page loads.  Effectively, this means that initialization script
  -	 * is stored inside the HTML &lt;body&gt; element's <code>onload</code>
  -	 * event handler.
  -	 */
  -	public void addInitializationScript(String script);
  -	
  -	/**
  -	 * Adds an external script.  The processor is expected to ensure
  -	 * that external scripts are only loaded a single time per page.
  -	 */
  -	
  -	public void addExternalScript(Resource resource);
  -	
  -	/**
  -	 * Ensures that the given string is unique.  The string
  -	 * is either returned unchanged, or a suffix is appended to
  -	 * ensure uniqueness.
  -	 */
  -	
  -	public String getUniqueString(String baseValue);
  -}
  +    /**
  +     * Adds scripting code to the main body. During the render, multiple scripts may render multiple
  +     * bodies; all are concatinated together to form a single block. The
  +     * {@link org.apache.tapestry.html.Body}&nbsp;component will write the body script contents
  +     * just inside the <code>&lt;body&gt;</code> tag.
  +     */
  +
  +    public void addBodyScript(String script);
  +
  +    /**
  +     * Adds initialization script. Initialization script is executed once, when the containing page
  +     * loads. Initialization script content is written only after all HTML content that could be
  +     * referenced from the script (in effect, just before the <code>&lt/body&gt; tag).
  +     */
  +    public void addInitializationScript(String script);
  +
  +    /**
  +     * Adds an external script. The processor is expected to ensure that external scripts are only
  +     * loaded a single time per page.
  +     */
  +
  +    public void addExternalScript(Resource resource);
  +
  +    /**
  +     * Ensures that the given string is unique. The string is either returned unchanged, or a suffix
  +     * is appended to ensure uniqueness.
  +     */
  +
  +    public String getUniqueString(String baseValue);
  +}
  \ No newline at end of file
  
  
  
  1.1                  jakarta-tapestry/framework/src/java/org/apache/tapestry/PageRenderSupport.java
  
  Index: PageRenderSupport.java
  ===================================================================
  // Copyright 2005 The Apache Software Foundation
  //
  // Licensed under the Apache License, Version 2.0 (the "License");
  // you may not use this file except in compliance with the License.
  // You may obtain a copy of the License at
  //
  //     http://www.apache.org/licenses/LICENSE-2.0
  //
  // Unless required by applicable law or agreed to in writing, software
  // distributed under the License is distributed on an "AS IS" BASIS,
  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  // See the License for the specific language governing permissions and
  // limitations under the License.
  
  package org.apache.tapestry;
  
  /**
   * Extends {@link org.apache.tapestry.IScriptProcessor}&nbsp;with a handful of additional methods
   * needed when rendering a page response.
   * 
   * @author Howard M. Lewis Ship
   * @since 3.1
   * @see org.apache.tapestry.html.Body
   * @see org.apache.tapestry.TapestryUtils#getPageRenderSupport(IRequestCycle, Object)
   */
  public interface PageRenderSupport extends IScriptProcessor
  {
      /**
       * Sets up the given URL to preload, and returns a reference to the loaded image, in the form of
       * a snippet of JavaScript expression that can be inserted into some larger block of JavaScript
       * as a function parameter, or as a property assignment. A typical return value might be
       * <code>tapestry_preload[7].src</code>.
       */
  
      public String getPreloadedImageReference(String url);
  }
  
  
  1.1                  jakarta-tapestry/framework/src/java/org/apache/tapestry/TapestryUtils.java
  
  Index: TapestryUtils.java
  ===================================================================
  // Copyright 2005 The Apache Software Foundation
  //
  // Licensed under the Apache License, Version 2.0 (the "License");
  // you may not use this file except in compliance with the License.
  // You may obtain a copy of the License at
  //
  //     http://www.apache.org/licenses/LICENSE-2.0
  //
  // Unless required by applicable law or agreed to in writing, software
  // distributed under the License is distributed on an "AS IS" BASIS,
  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  // See the License for the specific language governing permissions and
  // limitations under the License.
  
  package org.apache.tapestry;
  
  import org.apache.hivemind.ApplicationRuntimeException;
  import org.apache.hivemind.HiveMind;
  import org.apache.hivemind.util.Defense;
  
  /**
   * Constants and static methods.
   * 
   * @author Howard M. Lewis Ship
   * @since 3.1
   */
  public class TapestryUtils
  {
      /**
       * Stores an attribute into the request cycle, verifying that no object with that key is already
       * present.
       * 
       * @param cycle
       *            the cycle to store the attribute into
       * @param key
       *            the key to store the attribute as
       * @param object
       *            the attribute value to store
       * @throws IllegalStateException
       *             if a non-null value has been stored into the cycle with the provided key.
       */
  
      public static void storeUniqueAttribute(IRequestCycle cycle, String key, Object object)
      {
          Defense.notNull(cycle, "cycle");
          Defense.notNull(key, "key");
          Defense.notNull(object, "object");
  
          Object existing = cycle.getAttribute(key);
          if (existing != null)
              throw new IllegalStateException(TapestryMessages.nonUniqueAttribute(
                      object,
                      key,
                      existing));
  
          cycle.setAttribute(key, object);
      }
  
      static final String PAGE_RENDER_SUPPORT_ATTRIBUTE = "org.apache.tapestry.PageRenderSupport";
  
      /**
       * Stores the support object using {@link #storeUniqueAttribute(IRequestCycle, String, Object)}.
       */
  
      public static void storeRenderPageSupport(IRequestCycle cycle, PageRenderSupport support)
      {
          storeUniqueAttribute(cycle, PAGE_RENDER_SUPPORT_ATTRIBUTE, support);
      }
  
      /**
       * Gets the previously stored {@link org.apache.tapestry.PageRenderSupport}object.
       * 
       * @param cycle
       *            the request cycle storing the support object
       * @param caller
       *            the caller of this method (used in exception messages)
       * @throws ApplicationRuntimeException
       *             if no support object has been stored
       */
  
      public static PageRenderSupport getPageRenderSupport(IRequestCycle cycle, Object caller)
      {
          Defense.notNull(cycle, "cycle");
  
          PageRenderSupport result = (PageRenderSupport) cycle
                  .getAttribute(PAGE_RENDER_SUPPORT_ATTRIBUTE);
  
          if (result == null)
              throw new ApplicationRuntimeException(TapestryMessages.noPageRenderSupport(), HiveMind
                      .getLocation(caller), null);
  
          return result;
      }
  
      public static void removePageRenderSupport(IRequestCycle cycle)
      {
          cycle.removeAttribute(PAGE_RENDER_SUPPORT_ATTRIBUTE);
      }
  
      /**
       * Returns the {@link PageRenderSupport}&nbsp;object if previously stored, or null otherwise.
       * This is used in the rare case that a component wishes to adjust its behavior based on whether
       * the page render support services are avaiable (typically, adjust for whether enclosed by a
       * Body component, or not).
       */
  
      public static PageRenderSupport getOptionalPageRenderSupport(IRequestCycle cycle)
      {
          return (PageRenderSupport) cycle.getAttribute(PAGE_RENDER_SUPPORT_ATTRIBUTE);
      }
  }
  
  
  1.4       +2 -0      jakarta-tapestry/framework/src/java/org/apache/tapestry/html/Body.jwc
  
  Index: Body.jwc
  ===================================================================
  RCS file: /home/cvs/jakarta-tapestry/framework/src/java/org/apache/tapestry/html/Body.jwc,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -r1.3 -r1.4
  --- Body.jwc	27 Jan 2005 05:43:13 -0000	1.3
  +++ Body.jwc	29 Mar 2005 13:35:42 -0000	1.4
  @@ -33,5 +33,7 @@
     </parameter>
    
     <reserved-parameter name="onload"/>
  +  
  +  <inject property="assetService" object="engine-service:asset"/>
       
   </component-specification>
  
  
  
  1.4       +25 -42    jakarta-tapestry/framework/src/java/org/apache/tapestry/html/Rollover.java
  
  Index: Rollover.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tapestry/framework/src/java/org/apache/tapestry/html/Rollover.java,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -r1.3 -r1.4
  --- Rollover.java	5 Jan 2005 23:17:22 -0000	1.3
  +++ Rollover.java	29 Mar 2005 13:35:42 -0000	1.4
  @@ -25,33 +25,30 @@
   import org.apache.tapestry.IMarkupWriter;
   import org.apache.tapestry.IRequestCycle;
   import org.apache.tapestry.IScript;
  +import org.apache.tapestry.PageRenderSupport;
   import org.apache.tapestry.Tapestry;
  +import org.apache.tapestry.TapestryUtils;
   import org.apache.tapestry.components.ILinkComponent;
   import org.apache.tapestry.components.LinkEventType;
   import org.apache.tapestry.engine.IScriptSource;
   
   /**
  - *  Combines a link component (such as {@link org.apache.tapestry.link.DirectLink}) 
  - *  with an &lt;img&gt; and JavaScript code
  - *  to create a rollover effect that works with both Netscape Navigator and 
  - *  Internet Explorer.
  - *
  - *  [<a href="../../../../../ComponentReference/Rollover.html">Component Reference</a>]
  - *
  - *
  - *  @author Howard Lewis Ship
  + * Combines a link component (such as {@link org.apache.tapestry.link.DirectLink}) with an
  + * &lt;img&gt; and JavaScript code to create a rollover effect that works with both Netscape
  + * Navigator and Internet Explorer. [ <a
  + * href="../../../../../ComponentReference/Rollover.html">Component Reference </a>]
    * 
  - **/
  + * @author Howard Lewis Ship
  + */
   
   public abstract class Rollover extends AbstractComponent
   {
       private IScript _parsedScript;
   
       /**
  -     *  Converts an {@link IAsset} binding into a usable URL.  Returns null
  -     *  if the binding does not exist or the binding's value is null.
  -     *
  -     **/
  +     * Converts an {@link IAsset}binding into a usable URL. Returns null if the binding does not
  +     * exist or the binding's value is null.
  +     */
   
       protected String getAssetURL(IAsset asset, IRequestCycle cycle)
       {
  @@ -75,23 +72,14 @@
           boolean dynamic = false;
           String imageName = null;
   
  -        Body body = Body.get(cycle);
  -        if (body == null)
  -            throw new ApplicationRuntimeException(
  -                Tapestry.getMessage("Rollover.must-be-contained-by-body"),
  -                this,
  -                null,
  -                null);
  +        PageRenderSupport pageRenderSupport = TapestryUtils.getPageRenderSupport(cycle, this);
   
  -        ILinkComponent serviceLink =
  -            (ILinkComponent) cycle.getAttribute(Tapestry.LINK_COMPONENT_ATTRIBUTE_NAME);
  +        ILinkComponent serviceLink = (ILinkComponent) cycle
  +                .getAttribute(Tapestry.LINK_COMPONENT_ATTRIBUTE_NAME);
   
           if (serviceLink == null)
  -            throw new ApplicationRuntimeException(
  -                Tapestry.getMessage("Rollover.must-be-contained-by-link"),
  -                this,
  -                null,
  -                null);
  +            throw new ApplicationRuntimeException(Tapestry
  +                    .getMessage("Rollover.must-be-contained-by-link"), this, null, null);
   
           boolean linkDisabled = serviceLink.isDisabled();
   
  @@ -128,7 +116,7 @@
               if (blurURL == null)
                   blurURL = imageURL;
   
  -            imageName = writeScript(cycle, body, serviceLink, focusURL, blurURL);
  +            imageName = writeScript(cycle, pageRenderSupport, serviceLink, focusURL, blurURL);
   
               writer.attribute("name", imageName);
           }
  @@ -146,9 +134,8 @@
               IEngine engine = getPage().getEngine();
               IScriptSource source = engine.getScriptSource();
   
  -            Resource scriptLocation =
  -                getSpecification().getSpecificationLocation().getRelativeResource(
  -                    "Rollover.script");
  +            Resource scriptLocation = getSpecification().getSpecificationLocation()
  +                    .getRelativeResource("Rollover.script");
   
               _parsedScript = source.getScript(scriptLocation);
           }
  @@ -156,16 +143,12 @@
           return _parsedScript;
       }
   
  -    private String writeScript(
  -        IRequestCycle cycle,
  -        Body body,
  -        ILinkComponent link,
  -        String focusURL,
  -        String blurURL)
  +    private String writeScript(IRequestCycle cycle, PageRenderSupport pageRenderSupport,
  +            ILinkComponent link, String focusURL, String blurURL)
       {
  -        String imageName = body.getUniqueString(getId());
  -        String focusImageURL = body.getPreloadedImageReference(focusURL);
  -        String blurImageURL = body.getPreloadedImageReference(blurURL);
  +        String imageName = pageRenderSupport.getUniqueString(getId());
  +        String focusImageURL = pageRenderSupport.getPreloadedImageReference(focusURL);
  +        String blurImageURL = pageRenderSupport.getPreloadedImageReference(blurURL);
   
           Map symbols = new HashMap();
   
  @@ -173,7 +156,7 @@
           symbols.put("focusImageURL", focusImageURL);
           symbols.put("blurImageURL", blurImageURL);
   
  -        getParsedScript().execute(cycle, body, symbols);
  +        getParsedScript().execute(cycle, pageRenderSupport, symbols);
   
           // Add attributes to the link to control mouse over/out.
           // Because the script is written before the <body> tag,
  
  
  
  1.6       +33 -224   jakarta-tapestry/framework/src/java/org/apache/tapestry/html/Body.java
  
  Index: Body.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tapestry/framework/src/java/org/apache/tapestry/html/Body.java,v
  retrieving revision 1.5
  retrieving revision 1.6
  diff -u -r1.5 -r1.6
  --- Body.java	27 Jan 2005 14:20:45 -0000	1.5
  +++ Body.java	29 Mar 2005 13:35:42 -0000	1.6
  @@ -14,21 +14,14 @@
   
   package org.apache.tapestry.html;
   
  -import java.util.ArrayList;
  -import java.util.HashMap;
  -import java.util.List;
  -import java.util.Map;
  -
  -import org.apache.hivemind.util.ClasspathResource;
  -import org.apache.hivemind.ApplicationRuntimeException;
   import org.apache.hivemind.Resource;
   import org.apache.tapestry.AbstractComponent;
   import org.apache.tapestry.IMarkupWriter;
   import org.apache.tapestry.IRequestCycle;
  -import org.apache.tapestry.IScriptProcessor;
  -import org.apache.tapestry.Tapestry;
  -import org.apache.tapestry.asset.PrivateAsset;
  -import org.apache.tapestry.util.IdAllocator;
  +import org.apache.tapestry.PageRenderSupport;
  +import org.apache.tapestry.TapestryUtils;
  +import org.apache.tapestry.engine.IEngineService;
  +import org.apache.tapestry.util.PageRenderSupportImpl;
   
   /**
    * The body of a Tapestry page. This is used since it allows components on the page access to an
  @@ -39,40 +32,9 @@
    * @author Howard Lewis Ship
    */
   
  -public abstract class Body extends AbstractComponent implements IScriptProcessor
  +public abstract class Body extends AbstractComponent implements PageRenderSupport
   {
  -    // Lines that belong inside the onLoad event handler for the <body> tag.
  -    private StringBuffer _initializationScript;
  -
  -    // Any other scripting desired
  -
  -    private StringBuffer _bodyScript;
  -
  -    // Contains text lines related to image initializations
  -
  -    private StringBuffer _imageInitializations;
  -
  -    /**
  -     * Map of URLs to Strings (preloaded image references).
  -     */
  -
  -    private Map _imageMap;
  -
  -    /**
  -     * List of included scripts. Values are Strings.
  -     * 
  -     * @since 1.0.5
  -     */
  -
  -    private List _externalScripts;
  -
  -    private IdAllocator _idAllocator;
  -
  -    private static final String ATTRIBUTE_NAME = "org.apache.tapestry.active.Body";
  -
  -    /**
  -     * Tracks a particular preloaded image.
  -     */
  +    private PageRenderSupportImpl _pageRenderSupport;
   
       /**
        * Adds to the script an initialization for the named variable as an Image(), to the given URL.
  @@ -85,33 +47,7 @@
   
       public String getPreloadedImageReference(String URL)
       {
  -        if (_imageMap == null)
  -            _imageMap = new HashMap();
  -
  -        String reference = (String) _imageMap.get(URL);
  -
  -        if (reference == null)
  -        {
  -            int count = _imageMap.size();
  -            String varName = "tapestry_preload[" + count + "]";
  -            reference = varName + ".src";
  -
  -            if (_imageInitializations == null)
  -                _imageInitializations = new StringBuffer();
  -
  -            _imageInitializations.append("  ");
  -            _imageInitializations.append(varName);
  -            _imageInitializations.append(" = new Image();\n");
  -            _imageInitializations.append("  ");
  -            _imageInitializations.append(reference);
  -            _imageInitializations.append(" = \"");
  -            _imageInitializations.append(URL);
  -            _imageInitializations.append("\";\n");
  -
  -            _imageMap.put(URL, reference);
  -        }
  -
  -        return reference;
  +        return _pageRenderSupport.getPreloadedImageReference(URL);
       }
   
       /**
  @@ -122,12 +58,7 @@
   
       public void addInitializationScript(String script)
       {
  -        if (_initializationScript == null)
  -            _initializationScript = new StringBuffer(script.length() + 1);
  -
  -        _initializationScript.append(script);
  -        _initializationScript.append('\n');
  -
  +        _pageRenderSupport.addInitializationScript(script);
       }
   
       /**
  @@ -149,10 +80,7 @@
   
       public void addBodyScript(String script)
       {
  -        if (_bodyScript == null)
  -            _bodyScript = new StringBuffer(script.length());
  -
  -        _bodyScript.append(script);
  +        _pageRenderSupport.addBodyScript(script);
       }
   
       /**
  @@ -165,70 +93,34 @@
   
       public void addExternalScript(Resource scriptLocation)
       {
  -        if (_externalScripts == null)
  -            _externalScripts = new ArrayList();
  -
  -        if (_externalScripts.contains(scriptLocation))
  -            return;
  -
  -        // Alas, this won't give a good ILocation for the actual problem.
  -
  -        if (!(scriptLocation instanceof ClasspathResource))
  -            throw new ApplicationRuntimeException(Tapestry.format(
  -                    "Body.include-classpath-script-only",
  -                    scriptLocation), this, null, null);
  -
  -        // Record the URL so we don't include it twice.
  -
  -        _externalScripts.add(scriptLocation);
  -    }
  -
  -    /**
  -     * Writes &lt;script&gt; elements for all the external scripts.
  -     */
  -    private void writeExternalScripts(IMarkupWriter writer)
  -    {
  -        int count = Tapestry.size(_externalScripts);
  -        for (int i = 0; i < count; i++)
  -        {
  -            ClasspathResource scriptLocation = (ClasspathResource) _externalScripts.get(i);
  -
  -            // This is still very awkward! Should move the code inside PrivateAsset somewhere
  -            // else, so that an asset does not have to be created to to build the URL.
  -            PrivateAsset asset = new PrivateAsset(scriptLocation, null);
  -            String url = asset.buildURL(getPage().getRequestCycle());
  -
  -            // Note: important to use begin(), not beginEmpty(), because browser don't
  -            // interpret <script .../> properly.
  -
  -            writer.begin("script");
  -            writer.attribute("language", "JavaScript");
  -            writer.attribute("type", "text/javascript");
  -            writer.attribute("src", url);
  -            writer.end();
  -            writer.println();
  -        }
  -
  +        _pageRenderSupport.addExternalScript(scriptLocation);
       }
   
       /**
        * Retrieves the <code>Body</code> that was stored into the request cycle. This allows
        * components wrapped by the <code>Body</code> to locate it and access the services it
        * provides.
  +     * 
  +     * @deprecated To be removed in 3.2. Use
  +     *             {@link org.apache.tapestry.TapestryUtils#getPageRenderSupport(IRequestCycle)}
  +     *             instead.
        */
   
       public static Body get(IRequestCycle cycle)
       {
  -        return (Body) cycle.getAttribute(ATTRIBUTE_NAME);
  +        return (Body) TapestryUtils.getPageRenderSupport(cycle, null);
       }
   
  -    protected void renderComponent(IMarkupWriter writer, IRequestCycle cycle)
  +    protected void prepareForRender(IRequestCycle cycle)
       {
  -        if (cycle.getAttribute(ATTRIBUTE_NAME) != null)
  -            throw new ApplicationRuntimeException(Tapestry.getMessage("Body.may-not-nest"), this,
  -                    null, null);
  +        super.prepareForRender(cycle);
   
  -        cycle.setAttribute(ATTRIBUTE_NAME, this);
  +        _pageRenderSupport = new PageRenderSupportImpl(getAssetService(), getLocation());
  +    }
  +
  +    protected void renderComponent(IMarkupWriter writer, IRequestCycle cycle)
  +    {
  +        TapestryUtils.storeRenderPageSupport(cycle, this);
   
           IMarkupWriter nested = writer.getNestedWriter();
   
  @@ -244,7 +136,7 @@
           // Write the page's scripting. This is included scripts
           // and dynamic JavaScript.
   
  -        writeScript(writer);
  +        _pageRenderSupport.writeBodyScript(writer, cycle);
   
           // Close the nested writer, which dumps its buffered content
           // into its parent.
  @@ -256,7 +148,7 @@
           // would create a window.onload event handler, but this is better
           // (it doesn't have to wait for external images to load).
   
  -        writeInitializationScript(writer);
  +        _pageRenderSupport.writeInitializationScript(writer);
   
           writer.end(); // <body>
       }
  @@ -265,99 +157,19 @@
       {
           super.cleanupAfterRender(cycle);
   
  -        if (_idAllocator != null)
  -            _idAllocator.clear();
  -
  -        if (_imageMap != null)
  -            _imageMap.clear();
  +        _pageRenderSupport = null;
   
  -        if (_externalScripts != null)
  -            _externalScripts.clear();
  -
  -        if (_initializationScript != null)
  -            _initializationScript.setLength(0);
  -
  -        if (_imageInitializations != null)
  -            _imageInitializations.setLength(0);
  -
  -        if (_bodyScript != null)
  -            _bodyScript.setLength(0);
  -    }
  -
  -    /**
  -     * Writes a single large JavaScript block containing:
  -     * <ul>
  -     * <li>Any image initializations
  -     * <li>Any scripting
  -     * <li>Any initializations
  -     * </ul>
  -     * <p>
  -     * The script is written into a nested markup writer.
  -     * <p>
  -     * If there are any other initializations (see {@link #addOtherInitialization(String)}), then a
  -     * function to execute them is created.
  -     */
  -
  -    private void writeScript(IMarkupWriter writer)
  -    {
  -        if (!Tapestry.isEmpty(_externalScripts))
  -            writeExternalScripts(writer);
  -
  -        if (!(any(_bodyScript) || any(_imageInitializations)))
  -            return;
  -
  -        writer.begin("script");
  -        writer.attribute("language", "JavaScript");
  -        writer.attribute("type", "text/javascript");
  -        writer.printRaw("<!--");
  -
  -        if (any(_imageInitializations))
  -        {
  -            writer.printRaw("\n\nvar tapestry_preload = new Array();\n");
  -            writer.printRaw("if (document.images)\n");
  -            writer.printRaw("{\n");
  -            writer.printRaw(_imageInitializations.toString());
  -            writer.printRaw("}\n");
  -        }
  -
  -        if (any(_bodyScript))
  -        {
  -            writer.printRaw("\n\n");
  -            writer.printRaw(_bodyScript.toString());
  -        }
  -
  -        writer.printRaw("\n\n// -->");
  -        writer.end();
  -    }
  -
  -    /** @since 3.1 */
  -    private void writeInitializationScript(IMarkupWriter writer)
  -    {
  -        if (!any(_initializationScript))
  -            return;
  -
  -        writer.begin("script");
  -        writer.attribute("language", "JavaScript");
  -        writer.attribute("type", "text/javascript");
  -        writer.printRaw("<!--\n");
  -
  -        writer.printRaw(_initializationScript.toString());
  -
  -        writer.printRaw("\n// -->");
  -        writer.end();
  -    }
  -
  -    private boolean any(StringBuffer buffer)
  -    {
  -        if (buffer == null)
  -            return false;
  -
  -        return buffer.length() > 0;
  +        TapestryUtils.removePageRenderSupport(cycle);
       }
   
       public abstract String getElement();
   
       public abstract void setElement(String element);
  +    
  +    /** Injected
  +     * @since 3.1
  +     */
  +    public abstract IEngineService getAssetService();
   
       /**
        * Sets the element parameter property to its default, "body".
  @@ -373,10 +185,7 @@
   
       public String getUniqueString(String baseValue)
       {
  -        if (_idAllocator == null)
  -            _idAllocator = new IdAllocator();
  -
  -        return _idAllocator.allocateId(baseValue);
  +        return _pageRenderSupport.getUniqueString(baseValue);
       }
   
   }
  \ No newline at end of file
  
  
  
  1.4       +26 -41    jakarta-tapestry/framework/src/java/org/apache/tapestry/html/Script.java
  
  Index: Script.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tapestry/framework/src/java/org/apache/tapestry/html/Script.java,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -r1.3 -r1.4
  --- Script.java	6 Jan 2005 02:17:23 -0000	1.3
  +++ Script.java	29 Mar 2005 13:35:42 -0000	1.4
  @@ -26,40 +26,37 @@
   import org.apache.tapestry.IMarkupWriter;
   import org.apache.tapestry.IRequestCycle;
   import org.apache.tapestry.IScript;
  +import org.apache.tapestry.PageRenderSupport;
   import org.apache.tapestry.Tapestry;
  +import org.apache.tapestry.TapestryUtils;
   import org.apache.tapestry.engine.IScriptSource;
   
   /**
  - *  Works with the {@link Body} component to add a script (and perhaps some initialization) 
  - *  to the HTML response.
  - *
  - *  [<a href="../../../../../ComponentReference/Script.html">Component Reference</a>]
  - *
  - *  @author Howard Lewis Ship
  - *
  - **/
  + * Works with the {@link Body}component to add a script (and perhaps some initialization) to the
  + * HTML response. [ <a href="../../../../../ComponentReference/Script.html">Component Reference
  + * </a>]
  + * 
  + * @author Howard Lewis Ship
  + */
   
   public abstract class Script extends AbstractComponent
   {
       private Map _baseSymbols;
   
       /**
  -     *  A Map of input and output symbols visible to the body of the Script.
  +     * A Map of input and output symbols visible to the body of the Script.
        * 
  -     *  @since 2.2
  -     * 
  -     **/
  +     * @since 2.2
  +     */
   
       private Map _symbols;
   
       /**
  -     *  Constructs the symbols {@link Map}.  This starts with the
  -     *  contents of the symbols parameter (if specified) to which is added
  -     *  any informal parameters.  If both a symbols parameter and informal
  -     *  parameters are bound, then a copy of the symbols parameter's value is made
  -     *  (that is, the {@link Map} provided by the symbols parameter is read, but not modified).
  -     *
  -     **/
  +     * Constructs the symbols {@link Map}. This starts with the contents of the symbols parameter
  +     * (if specified) to which is added any informal parameters. If both a symbols parameter and
  +     * informal parameters are bound, then a copy of the symbols parameter's value is made (that is,
  +     * the {@link Map}provided by the symbols parameter is read, but not modified).
  +     */
   
       private Map getInputSymbols()
       {
  @@ -69,7 +66,7 @@
               result.putAll(_baseSymbols);
   
           // Now, iterate through all the binding names (which includes both
  -        // formal and informal parmeters).  Skip the formal ones and
  +        // formal and informal parmeters). Skip the formal ones and
           // access the informal ones.
   
           Iterator i = getBindingNames().iterator();
  @@ -93,10 +90,8 @@
       }
   
       /**
  -     *  Gets the {@link IScript} for the correct script.
  -     *
  -     *
  -     **/
  +     * Gets the {@link IScript}for the correct script.
  +     */
   
       private IScript getParsedScript(IRequestCycle cycle)
       {
  @@ -111,8 +106,7 @@
           // If the script path is relative, it should be relative to the Script component's
           // container (i.e., relative to a page in the application).
   
  -        Resource rootLocation =
  -            getContainer().getSpecification().getSpecificationLocation();
  +        Resource rootLocation = getContainer().getSpecification().getSpecificationLocation();
           Resource scriptLocation = rootLocation.getRelativeResource(scriptPath);
   
           try
  @@ -130,18 +124,11 @@
       {
           if (!cycle.isRewinding())
           {
  -            Body body = Body.get(cycle);
  -
  -            if (body == null)
  -                throw new ApplicationRuntimeException(
  -                    Tapestry.getMessage("Script.must-be-contained-by-body"),
  -                    this,
  -                    null,
  -                    null);
  +            PageRenderSupport pageRenderSupport = TapestryUtils.getPageRenderSupport(cycle, this);
   
               _symbols = getInputSymbols();
   
  -            getParsedScript(cycle).execute(cycle, body, _symbols);
  +            getParsedScript(cycle).execute(cycle, pageRenderSupport, _symbols);
           }
   
           // Render the body of the Script;
  @@ -161,13 +148,11 @@
       }
   
       /**
  -     *  Returns the complete set of symbols (input and output)
  -     *  from the script execution.  This is visible to the body
  -     *  of the Script, but is cleared after the Script
  -     *  finishes rendering.
  +     * Returns the complete set of symbols (input and output) from the script execution. This is
  +     * visible to the body of the Script, but is cleared after the Script finishes rendering.
        * 
  -     *  @since 2.2
  -     **/
  +     * @since 2.2
  +     */
   
       public Map getSymbols()
       {
  
  
  
  1.7       +4 -6      jakarta-tapestry/contrib/src/java/org/apache/tapestry/contrib/inspector/InspectorButton.java
  
  Index: InspectorButton.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tapestry/contrib/src/java/org/apache/tapestry/contrib/inspector/InspectorButton.java,v
  retrieving revision 1.6
  retrieving revision 1.7
  diff -u -r1.6 -r1.7
  --- InspectorButton.java	6 Feb 2005 15:08:50 -0000	1.6
  +++ InspectorButton.java	29 Mar 2005 13:35:42 -0000	1.7
  @@ -25,7 +25,9 @@
   import org.apache.tapestry.IMarkupWriter;
   import org.apache.tapestry.IRequestCycle;
   import org.apache.tapestry.IScript;
  +import org.apache.tapestry.PageRenderSupport;
   import org.apache.tapestry.Tapestry;
  +import org.apache.tapestry.TapestryUtils;
   import org.apache.tapestry.engine.DirectServiceParameter;
   import org.apache.tapestry.engine.IEngineService;
   import org.apache.tapestry.engine.ILink;
  @@ -89,13 +91,9 @@
   
           symbols.put("URL", link.getURL());
   
  -        Body body = Body.get(cycle);
  +        PageRenderSupport pageRenderSupport = TapestryUtils.getPageRenderSupport(cycle, this);
   
  -        if (body == null)
  -            throw new ApplicationRuntimeException(Tapestry
  -                    .getMessage("InspectorButton.must-be-contained-by-body"), this, null, null);
  -
  -        script.execute(cycle, body, symbols);
  +        script.execute(cycle, pageRenderSupport, symbols);
   
           // Now, go render the rest from the template.
   
  
  
  
  1.3       +11 -1     jakarta-tapestry/framework/src/java/org/apache/tapestry/asset/ClasspathAssetFactory.java
  
  Index: ClasspathAssetFactory.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tapestry/framework/src/java/org/apache/tapestry/asset/ClasspathAssetFactory.java,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- ClasspathAssetFactory.java	6 Jan 2005 02:17:19 -0000	1.2
  +++ ClasspathAssetFactory.java	29 Mar 2005 13:35:42 -0000	1.3
  @@ -21,12 +21,18 @@
   import org.apache.hivemind.Resource;
   import org.apache.hivemind.util.ClasspathResource;
   import org.apache.tapestry.IAsset;
  +import org.apache.tapestry.engine.IEngineService;
   
   /**
  + * Creates instances of {@link org.apache.tapestry.asset.PrivateAsset}, which are the holders of
  + * classpath: resources.
  + * 
    * @author Howard M. Lewis Ship
  + * @since 3.1
    */
   public class ClasspathAssetFactory implements AssetFactory
   {
  +    private IEngineService _assetService;
   
       public IAsset createAsset(Resource baseResource, String path, Locale locale, Location location)
       {
  @@ -37,7 +43,11 @@
               throw new ApplicationRuntimeException(AssetMessages.missingAsset(path, baseResource),
                       location, null);
   
  -        return new PrivateAsset((ClasspathResource) localized, location);
  +        return new PrivateAsset((ClasspathResource) localized, _assetService, location);
       }
   
  +    public void setAssetService(IEngineService assetService)
  +    {
  +        _assetService = assetService;
  +    }
   }
  \ No newline at end of file
  
  
  
  1.6       +24 -5     jakarta-tapestry/framework/src/java/org/apache/tapestry/asset/PrivateAsset.java
  
  Index: PrivateAsset.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tapestry/framework/src/java/org/apache/tapestry/asset/PrivateAsset.java,v
  retrieving revision 1.5
  retrieving revision 1.6
  diff -u -r1.5 -r1.6
  --- PrivateAsset.java	5 Jan 2005 23:17:24 -0000	1.5
  +++ PrivateAsset.java	29 Mar 2005 13:35:42 -0000	1.6
  @@ -17,10 +17,10 @@
   import java.io.InputStream;
   import java.net.URL;
   
  -import org.apache.hivemind.util.ClasspathResource;
   import org.apache.hivemind.ApplicationRuntimeException;
   import org.apache.hivemind.Location;
   import org.apache.hivemind.Resource;
  +import org.apache.hivemind.util.ClasspathResource;
   import org.apache.tapestry.IRequestCycle;
   import org.apache.tapestry.Tapestry;
   import org.apache.tapestry.engine.IEngineService;
  @@ -37,9 +37,23 @@
   
   public class PrivateAsset extends AbstractAsset
   {
  +    private IEngineService _assetService;
  +
  +    /**
  +     * @deprecated To be removed (someday). Use
  +     *             {@link #PrivateAsset(ClasspathResource, IEngineService, Location)}&nbsp;instead.
  +     */
       public PrivateAsset(ClasspathResource resourceLocation, Location location)
       {
  +        this(resourceLocation, null, location);
  +    }
  +
  +    public PrivateAsset(ClasspathResource resourceLocation, IEngineService assetService,
  +            Location location)
  +    {
           super(resourceLocation, location);
  +
  +        _assetService = assetService;
       }
   
       /**
  @@ -50,11 +64,16 @@
   
       public String buildURL(IRequestCycle cycle)
       {
  -        IEngineService service = cycle.getEngine().getService(Tapestry.ASSET_SERVICE);
  +        String path = getResourceLocation().getPath();
  +
  +        // ClasspathAssetFactory will provide the asset service as a constructor
  +        // parameter, but there are a few odd uses of PrivateAsset where this
  +        // is not handy.
  +
  +        if (_assetService == null)
  +            _assetService = cycle.getEngine().getService(Tapestry.ASSET_SERVICE);
   
  -       String path = getResourceLocation().getPath();
  - 
  -        ILink link = service.getLink(cycle, path);
  +        ILink link = _assetService.getLink(cycle, path);
   
           return link.getURL();
       }
  
  
  
  1.4       +6 -5      jakarta-tapestry/framework/src/java/org/apache/tapestry/valid/BaseValidator.java
  
  Index: BaseValidator.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tapestry/framework/src/java/org/apache/tapestry/valid/BaseValidator.java,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -r1.3 -r1.4
  --- BaseValidator.java	6 Jan 2005 02:17:23 -0000	1.3
  +++ BaseValidator.java	29 Mar 2005 13:35:42 -0000	1.4
  @@ -29,7 +29,9 @@
   import org.apache.tapestry.IMarkupWriter;
   import org.apache.tapestry.IRequestCycle;
   import org.apache.tapestry.IScript;
  +import org.apache.tapestry.PageRenderSupport;
   import org.apache.tapestry.Tapestry;
  +import org.apache.tapestry.TapestryUtils;
   import org.apache.tapestry.engine.IScriptSource;
   import org.apache.tapestry.form.FormEventType;
   import org.apache.tapestry.form.IFormComponent;
  @@ -274,13 +276,12 @@
   
           IScript script = source.getScript(location);
   
  -        Body body = Body.get(cycle);
  +        // If there's an error, report it against the field (this validator object doesn't
  +        // have a location).
   
  -        if (body == null)
  -            throw new ApplicationRuntimeException(Tapestry
  -                    .getMessage("ValidField.must-be-contained-by-body"), field, null, null);
  +        PageRenderSupport pageRenderSupport = TapestryUtils.getPageRenderSupport(cycle, field);
   
  -        script.execute(cycle, body, finalSymbols);
  +        script.execute(cycle, pageRenderSupport, finalSymbols);
   
           String functionName = (String) finalSymbols.get(FUNCTION_SYMBOL);
   
  
  
  
  1.4       +8 -6      jakarta-tapestry/framework/src/java/org/apache/tapestry/valid/ValidField.java
  
  Index: ValidField.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tapestry/framework/src/java/org/apache/tapestry/valid/ValidField.java,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -r1.3 -r1.4
  --- ValidField.java	6 Jan 2005 02:17:23 -0000	1.3
  +++ ValidField.java	29 Mar 2005 13:35:42 -0000	1.4
  @@ -19,7 +19,9 @@
   import org.apache.tapestry.IForm;
   import org.apache.tapestry.IMarkupWriter;
   import org.apache.tapestry.IRequestCycle;
  +import org.apache.tapestry.PageRenderSupport;
   import org.apache.tapestry.Tapestry;
  +import org.apache.tapestry.TapestryUtils;
   import org.apache.tapestry.form.AbstractTextField;
   import org.apache.tapestry.form.Form;
   import org.apache.tapestry.form.IFormComponent;
  @@ -112,8 +114,8 @@
   
       /**
        * Creates JavaScript to set the cursor on the first required or error field encountered while
  -     * rendering. This only works if the text field is wrapped by a {@link Body}component (which is
  -     * almost always true).
  +     * rendering. This only works if the text field is wrapped by a {@link Body}&nbsp;component
  +     * (which is almost always true).
        */
   
       protected void addSelect(IRequestCycle cycle)
  @@ -123,11 +125,11 @@
           if (cycle.getAttribute(SELECTED_ATTRIBUTE_NAME) != null)
               return;
   
  -        Body body = Body.get(cycle);
  +        PageRenderSupport pageRenderSupport = TapestryUtils.getOptionalPageRenderSupport(cycle);
   
           // If not wrapped by a Body, then do nothing.
   
  -        if (body == null)
  +        if (pageRenderSupport == null)
               return;
   
           IForm form = Form.get(cycle);
  @@ -137,8 +139,8 @@
   
           String fullName = "document." + formName + "." + textFieldName;
   
  -        body.addInitializationScript(fullName + ".focus();");
  -        body.addInitializationScript(fullName + ".select();");
  +        pageRenderSupport.addInitializationScript(fullName + ".focus();");
  +        pageRenderSupport.addInitializationScript(fullName + ".select();");
   
           // Put a marker in, indicating that the selected field is known.
   
  
  
  
  1.19      +16 -6     jakarta-tapestry/eclipse/Tapestry-Workbench.launch
  
  Index: Tapestry-Workbench.launch
  ===================================================================
  RCS file: /home/cvs/jakarta-tapestry/eclipse/Tapestry-Workbench.launch,v
  retrieving revision 1.18
  retrieving revision 1.19
  diff -u -r1.18 -r1.19
  --- Tapestry-Workbench.launch	22 Mar 2005 13:40:54 -0000	1.18
  +++ Tapestry-Workbench.launch	29 Mar 2005 13:35:42 -0000	1.19
  @@ -1,8 +1,11 @@
   <?xml version="1.0" encoding="UTF-8"?>
   <launchConfiguration type="org.eclipse.jdt.launching.localJavaApplication">
   <booleanAttribute key="org.eclipse.jdt.launching.DEFAULT_CLASSPATH" value="false"/>
  +<booleanAttribute key="yk-capture-cpu-on-exit" value="true"/>
   <stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="org.mortbay.jetty.Server"/>
  -<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="jetty.xml"/>
  +<booleanAttribute key="yk-startup-with-sampling" value="true"/>
  +<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="src/config/jetty.xml"/>
  +<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Dorg.apache.tapestry.disable-caching=false"/>
   <listAttribute key="org.eclipse.jdt.launching.SOURCE_PATH">
   <listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt; &lt;runtimeClasspathEntry containerPath=&quot;JRE_LIB&quot; path=&quot;2&quot;     sourceAttachmentPath=&quot;JRE_SRC&quot; sourceRootPath=&quot;JRE_SRCROOT&quot; type=&quot;3&quot;/&gt; "/>
   <listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt; &lt;runtimeClasspathEntry internalArchive=&quot;/jakarta-tapestry/config&quot;     path=&quot;3&quot; type=&quot;2&quot;/&gt; "/>
  @@ -32,19 +35,26 @@
   <listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt; &lt;runtimeClasspathEntry containerPath=&quot;JDK_DIR/lib/tools.jar&quot; path=&quot;3&quot; type=&quot;3&quot;/&gt; "/>
   <listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt; &lt;runtimeClasspathEntry containerPath=&quot;JETTY_DIR/ext/ant.jar&quot; path=&quot;3&quot; type=&quot;3&quot;/&gt; "/>
   </listAttribute>
  -<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Dorg.apache.tapestry.disable-caching=true"/>
  -<stringAttribute key="org.eclipse.jdt.launching.WORKING_DIRECTORY" value="jakarta-tapestry/examples/Workbench"/>
  +<stringAttribute key="yk-snapshot-dir" value=""/>
   <booleanAttribute key="org.eclipse.debug.core.appendEnvironmentVariables" value="true"/>
  -<stringAttribute key="org.eclipse.debug.ui.target_run_perspective" value="perspective_default"/>
  +<stringAttribute key="org.eclipse.jdt.launching.WORKING_DIRECTORY" value="jakarta-tapestry/examples/Workbench"/>
   <booleanAttribute key="org.eclipse.jdt.launching.DEFAULT_SOURCE_PATH" value="false"/>
  -<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="jakarta-tapestry"/>
  +<stringAttribute key="org.eclipse.debug.ui.target_run_perspective" value="perspective_default"/>
  +<booleanAttribute key="yk-capture-memory-on-exit" value="false"/>
   <listAttribute key="org.eclipse.jdt.launching.CLASSPATH">
   <listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;&#10;&lt;runtimeClasspathEntry type=&quot;3&quot; path=&quot;1&quot; containerPath=&quot;JRE_LIB&quot;/&gt;&#10;"/>
   <listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;&#10;&lt;runtimeClasspathEntry id=&quot;org.eclipse.jdt.launching.classpathentry.defaultClasspath&quot;&gt;&#10;&lt;memento project=&quot;jakarta-tapestry&quot;/&gt;&#10;&lt;/runtimeClasspathEntry&gt;&#10;"/>
   <listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;&#10;&lt;runtimeClasspathEntry type=&quot;3&quot; path=&quot;3&quot; containerPath=&quot;JETTY_DIR/lib/javax.servlet.jar&quot;/&gt;&#10;"/>
   <listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;&#10;&lt;runtimeClasspathEntry type=&quot;3&quot; path=&quot;3&quot; containerPath=&quot;JETTY_DIR/lib/org.mortbay.jetty.jar&quot;/&gt;&#10;"/>
  -<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;&#10;&lt;runtimeClasspathEntry type=&quot;3&quot; path=&quot;3&quot; containerPath=&quot;JETTY_DIR/lib/org.mortbay.jetty-jdk1.2.jar&quot;/&gt;&#10;"/>
   <listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;&#10;&lt;runtimeClasspathEntry type=&quot;3&quot; path=&quot;3&quot; containerPath=&quot;JETTY_DIR/lib/org.mortbay.jmx.jar&quot;/&gt;&#10;"/>
  +<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;&#10;&lt;runtimeClasspathEntry type=&quot;3&quot; path=&quot;3&quot; containerPath=&quot;JETTY_DIR/ext/ant.jar&quot;/&gt;&#10;"/>
  +<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;&#10;&lt;runtimeClasspathEntry type=&quot;3&quot; path=&quot;3&quot; containerPath=&quot;JETTY_DIR/ext/crimson.jar&quot;/&gt;&#10;"/>
  +<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;&#10;&lt;runtimeClasspathEntry type=&quot;3&quot; path=&quot;3&quot; containerPath=&quot;JETTY_DIR/ext/jasper-compiler.jar&quot;/&gt;&#10;"/>
  +<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;&#10;&lt;runtimeClasspathEntry type=&quot;3&quot; path=&quot;3&quot; containerPath=&quot;JETTY_DIR/ext/jasper-runtime.jar&quot;/&gt;&#10;"/>
  +<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;&#10;&lt;runtimeClasspathEntry type=&quot;3&quot; path=&quot;3&quot; containerPath=&quot;JETTY_DIR/ext/javax.xml.jaxp.jar&quot;/&gt;&#10;"/>
   </listAttribute>
  +<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="jakarta-tapestry"/>
   <stringAttribute key="org.eclipse.debug.ui.target_debug_perspective" value="perspective_default"/>
  +<booleanAttribute key="yk-startup-with-tracing" value="false"/>
  +<booleanAttribute key="yk-startup-with-object-allocations" value="false"/>
   </launchConfiguration>
  
  
  
  1.6       +1 -1      jakarta-tapestry/framework/src/scripts/TestLinkRenderers.xml
  
  Index: TestLinkRenderers.xml
  ===================================================================
  RCS file: /home/cvs/jakarta-tapestry/framework/src/scripts/TestLinkRenderers.xml,v
  retrieving revision 1.5
  retrieving revision 1.6
  diff -u -r1.5 -r1.6
  --- TestLinkRenderers.xml	31 Jan 2005 20:35:47 -0000	1.5
  +++ TestLinkRenderers.xml	29 Mar 2005 13:35:42 -0000	1.6
  @@ -113,7 +113,7 @@
       </assert-output>
   
       <assert-output name="Message">
  - 		A link component with multiple functions for a single event type must be contained within a Body.		
  +  No PageRenderSupport object has been stored into the request cycle. This object is typically provided by a Body component. You should add a Body component to your template.
       </assert-output>
   
     </request>
  
  
  
  1.4       +1 -1      jakarta-tapestry/framework/src/scripts/TestValidFieldNoBody.xml
  
  Index: TestValidFieldNoBody.xml
  ===================================================================
  RCS file: /home/cvs/jakarta-tapestry/framework/src/scripts/TestValidFieldNoBody.xml,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -r1.3 -r1.4
  --- TestValidFieldNoBody.xml	6 Jan 2005 02:17:19 -0000	1.3
  +++ TestValidFieldNoBody.xml	29 Mar 2005 13:35:42 -0000	1.4
  @@ -39,7 +39,7 @@
   		</assert-output>
   	
   		<assert-output name="Message">
  -		A ValidField using client-side validation must be enclosed by a Body component.
  +    No PageRenderSupport object has been stored into the request cycle. This object is typically provided by a Body component. You should add a Body component to your template.
   		</assert-output>
   	
   	</request>
  
  
  
  1.6       +13 -13    jakarta-tapestry/framework/src/java/org/apache/tapestry/link/AbstractLinkComponent.java
  
  Index: AbstractLinkComponent.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tapestry/framework/src/java/org/apache/tapestry/link/AbstractLinkComponent.java,v
  retrieving revision 1.5
  retrieving revision 1.6
  diff -u -r1.5 -r1.6
  --- AbstractLinkComponent.java	6 Jan 2005 02:17:28 -0000	1.5
  +++ AbstractLinkComponent.java	29 Mar 2005 13:35:42 -0000	1.6
  @@ -20,16 +20,15 @@
   import java.util.List;
   import java.util.Map;
   
  -import org.apache.hivemind.ApplicationRuntimeException;
   import org.apache.tapestry.AbstractComponent;
   import org.apache.tapestry.IMarkupWriter;
   import org.apache.tapestry.IRequestCycle;
  -import org.apache.tapestry.Tapestry;
  +import org.apache.tapestry.PageRenderSupport;
  +import org.apache.tapestry.TapestryUtils;
   import org.apache.tapestry.components.ILinkComponent;
   import org.apache.tapestry.components.LinkEventType;
   import org.apache.tapestry.engine.IEngineService;
   import org.apache.tapestry.engine.ILink;
  -import org.apache.tapestry.html.Body;
   
   /**
    * Base class for implementations of {@link ILinkComponent}. Includes a disabled attribute (that
  @@ -110,11 +109,7 @@
           if (_eventHandlers == null)
               return;
   
  -        Body body = Body.get(cycle);
  -
  -        if (body == null)
  -            throw new ApplicationRuntimeException(Tapestry
  -                    .getMessage("AbstractLinkComponent.events-need-body"), this, null, null);
  +        PageRenderSupport pageRenderSupport = TapestryUtils.getPageRenderSupport(cycle, this);
   
           Iterator i = _eventHandlers.entrySet().iterator();
   
  @@ -123,13 +118,18 @@
               Map.Entry entry = (Map.Entry) i.next();
               LinkEventType type = (LinkEventType) entry.getKey();
   
  -            name = writeEventHandler(writer, body, name, type.getAttributeName(), entry.getValue());
  +            name = writeEventHandler(
  +                    writer,
  +                    pageRenderSupport,
  +                    name,
  +                    type.getAttributeName(),
  +                    entry.getValue());
           }
   
       }
   
  -    protected String writeEventHandler(IMarkupWriter writer, Body body, String name,
  -            String attributeName, Object value)
  +    protected String writeEventHandler(IMarkupWriter writer, PageRenderSupport pageRenderSupport,
  +            String name, String attributeName, Object value)
       {
           String wrapperFunctionName;
   
  @@ -139,7 +139,7 @@
           }
           else
           {
  -            String finalName = name == null ? body.getUniqueString("Link") : name;
  +            String finalName = name == null ? pageRenderSupport.getUniqueString("Link") : name;
   
               wrapperFunctionName = attributeName + "_" + finalName;
   
  @@ -160,7 +160,7 @@
   
               buffer.append("}\n\n");
   
  -            body.addBodyScript(buffer.toString());
  +            pageRenderSupport.addBodyScript(buffer.toString());
           }
   
           writer.attribute(attributeName, "javascript:" + wrapperFunctionName + "();");
  
  
  
  1.1                  jakarta-tapestry/framework/src/test/org/apache/tapestry/TestTapestryUtils.java
  
  Index: TestTapestryUtils.java
  ===================================================================
  // Copyright 2005 The Apache Software Foundation
  //
  // Licensed under the Apache License, Version 2.0 (the "License");
  // you may not use this file except in compliance with the License.
  // You may obtain a copy of the License at
  //
  //     http://www.apache.org/licenses/LICENSE-2.0
  //
  // Unless required by applicable law or agreed to in writing, software
  // distributed under the License is distributed on an "AS IS" BASIS,
  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  // See the License for the specific language governing permissions and
  // limitations under the License.
  
  package org.apache.tapestry;
  
  import org.apache.hivemind.ApplicationRuntimeException;
  import org.apache.hivemind.Location;
  import org.apache.hivemind.test.HiveMindTestCase;
  import org.easymock.MockControl;
  
  /**
   * Tests for {@link org.apache.tapestry.TapestryUtils}.
   * 
   * @author Howard M. Lewis Ship
   * @since 3.1
   */
  public class TestTapestryUtils extends HiveMindTestCase
  {
      private IRequestCycle newCycle(String key, Object attribute)
      {
          MockControl control = newControl(IRequestCycle.class);
          IRequestCycle cycle = (IRequestCycle) control.getMock();
  
          cycle.getAttribute(key);
          control.setReturnValue(attribute);
  
          return cycle;
      }
  
      public void testStoreUniqueAttributeSuccess()
      {
          Object newInstance = new Object();
  
          MockControl control = newControl(IRequestCycle.class);
          IRequestCycle cycle = (IRequestCycle) control.getMock();
  
          String key = "foo.bar.Baz";
  
          cycle.getAttribute(key);
          control.setReturnValue(null);
  
          cycle.setAttribute(key, newInstance);
  
          replayControls();
  
          TapestryUtils.storeUniqueAttribute(cycle, key, newInstance);
  
          verifyControls();
      }
  
      public void testStoreUniqueAttributeFailure()
      {
          Object existing = "*EXISTING*";
          Object newInstance = "*NEW*";
  
          String key = "foo.bar.Baz";
  
          IRequestCycle cycle = newCycle(key, existing);
  
          replayControls();
  
          try
          {
              TapestryUtils.storeUniqueAttribute(cycle, key, newInstance);
              unreachable();
          }
          catch (IllegalStateException ex)
          {
              assertEquals(TapestryMessages.nonUniqueAttribute(newInstance, key, existing), ex
                      .getMessage());
          }
  
          verifyControls();
      }
  
      public void testGetPageRenderSupportSuccess()
      {
          PageRenderSupport support = (PageRenderSupport) newMock(PageRenderSupport.class);
          IRequestCycle cycle = newCycle(TapestryUtils.PAGE_RENDER_SUPPORT_ATTRIBUTE, support);
  
          replayControls();
  
          PageRenderSupport actual = TapestryUtils.getPageRenderSupport(cycle, null);
  
          assertSame(support, actual);
  
          verifyControls();
      }
  
      public void testGetPageRenderSupportFailure()
      {
          Location l = fabricateLocation(22);
          IRequestCycle cycle = newCycle(TapestryUtils.PAGE_RENDER_SUPPORT_ATTRIBUTE, null);
  
          replayControls();
  
          try
          {
              TapestryUtils.getPageRenderSupport(cycle, l);
              unreachable();
          }
          catch (ApplicationRuntimeException ex)
          {
              assertEquals(TapestryMessages.noPageRenderSupport(), ex.getMessage());
              assertSame(l, ex.getLocation());
          }
  
          verifyControls();
      }
  
  }
  
  
  1.10      +5 -2      jakarta-tapestry/framework/src/descriptor/META-INF/tapestry.asset.xml
  
  Index: tapestry.asset.xml
  ===================================================================
  RCS file: /home/cvs/jakarta-tapestry/framework/src/descriptor/META-INF/tapestry.asset.xml,v
  retrieving revision 1.9
  retrieving revision 1.10
  diff -u -r1.9 -r1.10
  --- tapestry.asset.xml	15 Mar 2005 16:44:17 -0000	1.9
  +++ tapestry.asset.xml	29 Mar 2005 13:35:42 -0000	1.10
  @@ -74,8 +74,11 @@
       AssetFactory used when the prefix is "classpath:" or the
       base resource is a ClasspathResource.
       
  -    <create-instance class="ClasspathAssetFactory"/>
  -    
  +    <invoke-factory>
  +      <construct class="ClasspathAssetFactory">
  +        <set-object property="assetService" value="engine-service:asset"/>
  +      </construct>
  +    </invoke-factory>
     </service-point>
     
     <configuration-point id="AssetFactoryAdapters" schema-id="hivemind.lib.AdapterRegistry"/>
  
  
  
  1.6       +49 -43    jakarta-tapestry/contrib/src/java/org/apache/tapestry/contrib/palette/Palette.java
  
  Index: Palette.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tapestry/contrib/src/java/org/apache/tapestry/contrib/palette/Palette.java,v
  retrieving revision 1.5
  retrieving revision 1.6
  diff -u -r1.5 -r1.6
  --- Palette.java	8 Mar 2005 15:36:38 -0000	1.5
  +++ Palette.java	29 Mar 2005 13:35:42 -0000	1.6
  @@ -30,7 +30,9 @@
   import org.apache.tapestry.IMarkupWriter;
   import org.apache.tapestry.IRequestCycle;
   import org.apache.tapestry.IScript;
  +import org.apache.tapestry.PageRenderSupport;
   import org.apache.tapestry.Tapestry;
  +import org.apache.tapestry.TapestryUtils;
   import org.apache.tapestry.components.Block;
   import org.apache.tapestry.engine.IScriptSource;
   import org.apache.tapestry.form.Form;
  @@ -160,36 +162,42 @@
    * <pre>
    * 
    *  
  - *   TABLE.tapestry-palette TH
  - *   {
  - *     font-size: 9pt;
  - *     font-weight: bold;
  - *     color: white;
  - *     background-color: #330066;
  - *     text-align: center;
  - *   }
  - *  
  - *   TD.available-cell SELECT
  - *   {
  - *     font-weight: normal;
  - *     background-color: #FFFFFF;
  - *     width: 200px;
  - *   }
  - *   
  - *   TD.selected-cell SELECT
  - *   {
  - *     font-weight: normal;
  - *     background-color: #FFFFFF;
  - *     width: 200px;
  - *   }
    *   
  - *   TABLE.tapestry-palette TD.controls
  - *   {
  - *     text-align: center;
  - *     vertical-align: middle;
  - *     width: 60px;
  - *   }
    *    
  + *     
  + *      TABLE.tapestry-palette TH
  + *      {
  + *        font-size: 9pt;
  + *        font-weight: bold;
  + *        color: white;
  + *        background-color: #330066;
  + *        text-align: center;
  + *      }
  + *     
  + *      TD.available-cell SELECT
  + *      {
  + *        font-weight: normal;
  + *        background-color: #FFFFFF;
  + *        width: 200px;
  + *      }
  + *      
  + *      TD.selected-cell SELECT
  + *      {
  + *        font-weight: normal;
  + *        background-color: #FFFFFF;
  + *        width: 200px;
  + *      }
  + *      
  + *      TABLE.tapestry-palette TD.controls
  + *      {
  + *        text-align: center;
  + *        vertical-align: middle;
  + *        width: 60px;
  + *      }
  + *       
  + *     
  + *    
  + *   
    *  
    * </pre>
    * 
  @@ -324,37 +332,35 @@
               _script = source.getScript(scriptResource);
           }
   
  -        Body body = Body.get(cycle);
  -        if (body == null)
  -            throw new ApplicationRuntimeException("Palette component must be wrapped by a Body.",
  -                    this, null, null);
  +        PageRenderSupport pageRenderSupport = TapestryUtils.getPageRenderSupport(cycle, this);
   
  -        setImage(body, cycle, "selectImage", getSelectImage());
  -        setImage(body, cycle, "selectDisabledImage", getSelectDisabledImage());
  -        setImage(body, cycle, "deselectImage", getDeselectImage());
  -        setImage(body, cycle, "deselectDisabledImage", getDeselectDisabledImage());
  +        setImage(pageRenderSupport, cycle, "selectImage", getSelectImage());
  +        setImage(pageRenderSupport, cycle, "selectDisabledImage", getSelectDisabledImage());
  +        setImage(pageRenderSupport, cycle, "deselectImage", getDeselectImage());
  +        setImage(pageRenderSupport, cycle, "deselectDisabledImage", getDeselectDisabledImage());
   
           if (isSortUser())
           {
  -            setImage(body, cycle, "upImage", getUpImage());
  -            setImage(body, cycle, "upDisabledImage", getUpDisabledImage());
  -            setImage(body, cycle, "downImage", getDownImage());
  -            setImage(body, cycle, "downDisabledImage", getDownDisabledImage());
  +            setImage(pageRenderSupport, cycle, "upImage", getUpImage());
  +            setImage(pageRenderSupport, cycle, "upDisabledImage", getUpDisabledImage());
  +            setImage(pageRenderSupport, cycle, "downImage", getDownImage());
  +            setImage(pageRenderSupport, cycle, "downDisabledImage", getDownDisabledImage());
           }
   
           _symbols.put("palette", this);
   
  -        _script.execute(cycle, body, _symbols);
  +        _script.execute(cycle, pageRenderSupport, _symbols);
       }
   
       /**
        * Extracts its asset URL, sets it up for preloading, and assigns the preload reference as a
        * script symbol.
        */
  -    private void setImage(Body body, IRequestCycle cycle, String symbolName, IAsset asset)
  +    private void setImage(PageRenderSupport pageRenderSupport, IRequestCycle cycle,
  +            String symbolName, IAsset asset)
       {
           String URL = asset.buildURL(cycle);
  -        String reference = body.getPreloadedImageReference(URL);
  +        String reference = pageRenderSupport.getPreloadedImageReference(URL);
   
           _symbols.put(symbolName, reference);
       }
  
  
  
  1.6       +4 -10     jakarta-tapestry/framework/src/java/org/apache/tapestry/form/DatePicker.java
  
  Index: DatePicker.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tapestry/framework/src/java/org/apache/tapestry/form/DatePicker.java,v
  retrieving revision 1.5
  retrieving revision 1.6
  diff -u -r1.5 -r1.6
  --- DatePicker.java	15 Mar 2005 16:44:17 -0000	1.5
  +++ DatePicker.java	29 Mar 2005 13:35:42 -0000	1.6
  @@ -23,7 +23,6 @@
   import java.util.Locale;
   import java.util.Map;
   
  -import org.apache.hivemind.ApplicationRuntimeException;
   import org.apache.hivemind.HiveMind;
   import org.apache.hivemind.Resource;
   import org.apache.tapestry.IAsset;
  @@ -31,9 +30,9 @@
   import org.apache.tapestry.IMarkupWriter;
   import org.apache.tapestry.IRequestCycle;
   import org.apache.tapestry.IScript;
  -import org.apache.tapestry.Tapestry;
  +import org.apache.tapestry.PageRenderSupport;
  +import org.apache.tapestry.TapestryUtils;
   import org.apache.tapestry.engine.IScriptSource;
  -import org.apache.tapestry.html.Body;
   
   /**
    * Provides a Form <tt>java.util.Date</tt> field component for selecting dates. [ <a
  @@ -123,12 +122,7 @@
   
           if (!cycle.isRewinding())
           {
  -            Body body = Body.get(cycle);
  -
  -            if (body == null)
  -                throw new ApplicationRuntimeException(Tapestry.format(
  -                        "must-be-contained-by-body",
  -                        "DatePicker"), this, null, null);
  +            PageRenderSupport pageRenderSupport = TapestryUtils.getPageRenderSupport(cycle, this);
   
               Locale locale = getPage().getLocale();
               DateFormatSymbols dfs = new DateFormatSymbols(locale);
  @@ -151,7 +145,7 @@
               symbols.put(SYM_FORMNAME, form.getName());
               symbols.put(SYM_VALUE, value);
   
  -            _script.execute(cycle, body, symbols);
  +            _script.execute(cycle, pageRenderSupport, symbols);
   
               writer.beginEmpty("input");
               writer.attribute("type", "text");
  
  
  
  1.11      +4 -6      jakarta-tapestry/framework/src/java/org/apache/tapestry/form/Form.java
  
  Index: Form.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tapestry/framework/src/java/org/apache/tapestry/form/Form.java,v
  retrieving revision 1.10
  retrieving revision 1.11
  diff -u -r1.10 -r1.11
  --- Form.java	27 Jan 2005 19:03:03 -0000	1.10
  +++ Form.java	29 Mar 2005 13:35:42 -0000	1.11
  @@ -32,9 +32,11 @@
   import org.apache.tapestry.IForm;
   import org.apache.tapestry.IMarkupWriter;
   import org.apache.tapestry.IRequestCycle;
  +import org.apache.tapestry.PageRenderSupport;
   import org.apache.tapestry.RenderRewoundException;
   import org.apache.tapestry.StaleLinkException;
   import org.apache.tapestry.Tapestry;
  +import org.apache.tapestry.TapestryUtils;
   import org.apache.tapestry.engine.ActionServiceParameter;
   import org.apache.tapestry.engine.DirectServiceParameter;
   import org.apache.tapestry.engine.IEngineService;
  @@ -464,11 +466,7 @@
           if (_events == null || _events.isEmpty())
               return;
   
  -        Body body = Body.get(cycle);
  -
  -        if (body == null)
  -            throw new ApplicationRuntimeException(FormMessages.formNeedsBodyForEventHandlers(),
  -                    this, null, null);
  +        PageRenderSupport pageRenderSupport = TapestryUtils.getPageRenderSupport(cycle, this);
   
           StringBuffer buffer = new StringBuffer();
   
  @@ -537,7 +535,7 @@
               buffer.append("\n\n");
           }
   
  -        body.addInitializationScript(buffer.toString());
  +        pageRenderSupport.addInitializationScript(buffer.toString());
       }
   
       /**
  
  
  
  1.6       +6 -7      jakarta-tapestry/framework/src/java/org/apache/tapestry/form/LinkSubmit.java
  
  Index: LinkSubmit.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tapestry/framework/src/java/org/apache/tapestry/form/LinkSubmit.java,v
  retrieving revision 1.5
  retrieving revision 1.6
  diff -u -r1.5 -r1.6
  --- LinkSubmit.java	8 Mar 2005 15:36:37 -0000	1.5
  +++ LinkSubmit.java	29 Mar 2005 13:35:42 -0000	1.6
  @@ -20,8 +20,9 @@
   import org.apache.tapestry.IForm;
   import org.apache.tapestry.IMarkupWriter;
   import org.apache.tapestry.IRequestCycle;
  +import org.apache.tapestry.PageRenderSupport;
   import org.apache.tapestry.Tapestry;
  -import org.apache.tapestry.html.Body;
  +import org.apache.tapestry.TapestryUtils;
   
   /**
    * Implements a component that submits its enclosing form via a JavaScript link. [ <a
  @@ -69,17 +70,15 @@
           {
               if (!rewinding)
               {
  -                Body body = Body.get(cycle);
   
  -                if (body == null)
  -                    throw new ApplicationRuntimeException(Tapestry.format(
  -                            "must-be-contained-by-body",
  -                            "LinkSubmit"), this, null, null);
  +                PageRenderSupport pageRenderSupport = TapestryUtils.getPageRenderSupport(
  +                        cycle,
  +                        this);
   
                   // make sure the submit function is on the page (once)
                   if (cycle.getAttribute(ATTRIBUTE_FUNCTION_NAME) == null)
                   {
  -                    body
  +                    pageRenderSupport
                               .addBodyScript("function submitLink(form, elementId) { form._linkSubmit.value = elementId; if (form.onsubmit == null || form.onsubmit()) form.submit(); }");
                       cycle.setAttribute(ATTRIBUTE_FUNCTION_NAME, this);
                   }
  
  
  
  1.2       +0 -1      jakarta-tapestry/framework/src/java/org/apache/tapestry/form/FormStrings.properties
  
  Index: FormStrings.properties
  ===================================================================
  RCS file: /home/cvs/jakarta-tapestry/framework/src/java/org/apache/tapestry/form/FormStrings.properties,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- FormStrings.properties	27 Jan 2005 18:40:29 -0000	1.1
  +++ FormStrings.properties	29 Mar 2005 13:35:42 -0000	1.2
  @@ -16,6 +16,5 @@
   form-id-mismatch=Rewind of form {0} expected allocated id #{1} to be ''{2}'', but was ''{3}'' (requested by component {4}).
   forms-may-not-nest=Forms may not be nested.
   form-too-few-ids=Rewind of form {0} expected {1} more form elements, starting with id ''{2}''.
  -form-needs-body-for-event-handlers=A Form with event handlers must be enclosed by a Body component.
   encoding-type-contention=Components within Form {0} have requested conflicting encoding types ''{1}'' and ''{2}''.
   
  
  
  
  1.2       +0 -5      jakarta-tapestry/framework/src/java/org/apache/tapestry/form/FormMessages.java
  
  Index: FormMessages.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tapestry/framework/src/java/org/apache/tapestry/form/FormMessages.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- FormMessages.java	27 Jan 2005 18:40:29 -0000	1.1
  +++ FormMessages.java	29 Mar 2005 13:35:42 -0000	1.2
  @@ -53,11 +53,6 @@
           return _formatter.format("form-too-few-ids", form.getExtendedId(), new Integer(remainingCount), nextExpectedId);
       }
   
  -    public static String formNeedsBodyForEventHandlers()
  -    {
  -        return _formatter.getMessage("form-needs-body-for-event-handlers");
  -    }
  -
       public static String encodingTypeContention(IComponent form, String establishedEncodingType,
               String newEncodingType)
       {
  
  
  
  1.1                  jakarta-tapestry/framework/src/test/org/apache/tapestry/util/TestPageRenderSupport.java
  
  Index: TestPageRenderSupport.java
  ===================================================================
  // Copyright 2005 The Apache Software Foundation
  //
  // Licensed under the Apache License, Version 2.0 (the "License");
  // you may not use this file except in compliance with the License.
  // You may obtain a copy of the License at
  //
  //     http://www.apache.org/licenses/LICENSE-2.0
  //
  // Unless required by applicable law or agreed to in writing, software
  // distributed under the License is distributed on an "AS IS" BASIS,
  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  // See the License for the specific language governing permissions and
  // limitations under the License.
  
  package org.apache.tapestry.util;
  
  import java.io.CharArrayWriter;
  import java.io.PrintWriter;
  
  import org.apache.hivemind.ClassResolver;
  import org.apache.hivemind.Location;
  import org.apache.hivemind.Resource;
  import org.apache.hivemind.impl.DefaultClassResolver;
  import org.apache.hivemind.test.HiveMindTestCase;
  import org.apache.hivemind.util.ClasspathResource;
  import org.apache.tapestry.IMarkupWriter;
  import org.apache.tapestry.IRequestCycle;
  import org.apache.tapestry.engine.IEngineService;
  import org.apache.tapestry.engine.ILink;
  import org.apache.tapestry.markup.AsciiMarkupFilter;
  import org.apache.tapestry.markup.MarkupWriterImpl;
  import org.easymock.MockControl;
  
  /**
   * Tests for {@link org.apache.tapestry.util.PageRenderSupportImpl}.
   * 
   * @author Howard M. Lewis Ship
   * @since 3.1
   */
  public class TestPageRenderSupport extends HiveMindTestCase
  {
      private IEngineService newService()
      {
          return (IEngineService) newMock(IEngineService.class);
      }
  
      private IRequestCycle newCycle()
      {
          return (IRequestCycle) newMock(IRequestCycle.class);
      }
  
      private ILink newLink(String URL)
      {
          MockControl control = newControl(ILink.class);
          ILink link = (ILink) control.getMock();
  
          link.getURL();
          control.setReturnValue(URL);
  
          return link;
      }
  
      private CharArrayWriter _writer;
  
      private IMarkupWriter newWriter()
      {
          _writer = new CharArrayWriter();
  
          return new MarkupWriterImpl("text/html", new PrintWriter(_writer), new AsciiMarkupFilter());
      }
  
      private void assertOutput(String[] expectedLines)
      {
          StringBuffer buffer = new StringBuffer();
          for (int i = 0; i < expectedLines.length; i++)
          {
              // Note: PageRenderSupport is a bit sloppy; a lot of code just uses \n for
              // a newline seperator, other parts go through IMarkupWriter.println() and get
              // a proper newline seperator (which may be different).
  
              if (i > 0)
                  buffer.append("\n");
  
              buffer.append(expectedLines[i]);
          }
  
          assertOutput(buffer.toString());
      }
  
      private void assertOutput(String expected)
      {
          String actual = _writer.toString();
  
          assertEquals(expected, actual);
  
          _writer.reset();
      }
  
      public void testGetLocation()
      {
          IEngineService service = newService();
          Location l = fabricateLocation(99);
  
          replayControls();
  
          PageRenderSupportImpl prs = new PageRenderSupportImpl(service, l);
  
          assertSame(l, prs.getLocation());
  
          verifyControls();
      }
  
      public void testGetPreloadedImageReference()
      {
          IEngineService service = newService();
          Location l = fabricateLocation(99);
          IRequestCycle cycle = newCycle();
          IMarkupWriter writer = newWriter();
  
          replayControls();
  
          PageRenderSupportImpl prs = new PageRenderSupportImpl(service, l);
  
          assertEquals("tapestry_preload[0].src", prs.getPreloadedImageReference("/foo/bar.gif"));
          assertEquals("tapestry_preload[1].src", prs.getPreloadedImageReference("/zip/zap.png"));
          assertEquals("tapestry_preload[0].src", prs.getPreloadedImageReference("/foo/bar.gif"));
  
          prs.addBodyScript("myBodyScript();");
  
          prs.writeBodyScript(writer, cycle);
  
          assertOutput(new String[]
          { "<script language=\"JavaScript\" type=\"text/javascript\"><!--", "",
                  "var tapestry_preload = new Array();", "if (document.images)", "{",
                  "  tapestry_preload[0] = new Image();",
                  "  tapestry_preload[0].src = \"/foo/bar.gif\";",
                  "  tapestry_preload[1] = new Image();",
                  "  tapestry_preload[1].src = \"/zip/zap.png\";", "}", "", "", "myBodyScript();",
                  "", "// --></script>" });
  
          verifyControls();
      }
  
      public void testAddBodyScript()
      {
          IEngineService service = newService();
          Location l = fabricateLocation(99);
          IRequestCycle cycle = newCycle();
          IMarkupWriter writer = newWriter();
  
          replayControls();
  
          PageRenderSupportImpl prs = new PageRenderSupportImpl(service, l);
  
          prs.addBodyScript("myBodyScript();");
  
          prs.writeBodyScript(writer, cycle);
  
          assertOutput(new String[]
          { "<script language=\"JavaScript\" type=\"text/javascript\"><!--", "", "myBodyScript();",
                  "", "// --></script>" });
  
          verifyControls();
      }
  
      public void testGetUniqueValue()
      {
          IEngineService service = newService();
          Location l = fabricateLocation(99);
  
          replayControls();
  
          PageRenderSupportImpl prs = new PageRenderSupportImpl(service, l);
  
          assertEquals("foo", prs.getUniqueString("foo"));
          assertEquals("foo$0", prs.getUniqueString("foo"));
          assertEquals("bar", prs.getUniqueString("bar"));
          assertEquals("foo$1", prs.getUniqueString("foo"));
  
          verifyControls();
      }
  
      public void testAddInitializationScript()
      {
          IEngineService service = newService();
          Location l = fabricateLocation(99);
          IMarkupWriter writer = newWriter();
  
          replayControls();
  
          PageRenderSupportImpl prs = new PageRenderSupportImpl(service, l);
  
          prs.addInitializationScript("myInitializationScript1();");
          prs.addInitializationScript("myInitializationScript2();");
  
          prs.writeInitializationScript(writer);
  
          assertOutput(new String[]
          { "<script language=\"JavaScript\" type=\"text/javascript\"><!--",
                  "myInitializationScript1();", "myInitializationScript2();", "", "// --></script>" });
  
          verifyControls();
      }
  
      public void testAddExternalScript() throws Exception
      {
          String newline = System.getProperty("line.separator");
  
          Location l = fabricateLocation(22);
          ClassResolver resolver = new DefaultClassResolver();
          Resource filea = new ClasspathResource(resolver, "org/apache/tapestry/utils/filea.txt");
          Resource fileb = new ClasspathResource(resolver, "org/apache/tapestry/utils/fileb.txt");
  
          MockControl assetServicec = newControl(IEngineService.class);
          IEngineService assetService = (IEngineService) assetServicec.getMock();
  
          IRequestCycle cycle = newCycle();
  
          assetService.getLink(cycle, "org/apache/tapestry/utils/filea.txt");
          assetServicec.setReturnValue(newLink("/app?filea.txt"));
  
          assetService.getLink(cycle, "org/apache/tapestry/utils/fileb.txt");
          assetServicec.setReturnValue(newLink("/app?fileb.txt"));
  
          IMarkupWriter writer = newWriter();
  
          replayControls();
  
          PageRenderSupportImpl prs = new PageRenderSupportImpl(assetService, l);
  
          prs.addExternalScript(filea);
          prs.addExternalScript(fileb);
          prs.addExternalScript(filea);
  
          prs.writeBodyScript(writer, cycle);
  
          // PageRenderSupport is a little sloppy about using \n for a newline, vs. using
          // the property line seperator sequence and it bites us right here.
  
          assertOutput(scriptTagFor("/app?filea.txt") + newline + scriptTagFor("/app?fileb.txt")
                  + newline);
  
          verifyControls();
      }
  
      private String scriptTagFor(String url)
      {
          return "<script language=\"JavaScript\" type=\"text/javascript\" src=\"" + url
                  + "\"></script>";
      }
  }
  
  
  1.1                  jakarta-tapestry/framework/src/java/org/apache/tapestry/util/PageRenderSupportImpl.java
  
  Index: PageRenderSupportImpl.java
  ===================================================================
  // Copyright 2005 The Apache Software Foundation
  //
  // Licensed under the Apache License, Version 2.0 (the "License");
  // you may not use this file except in compliance with the License.
  // You may obtain a copy of the License at
  //
  //     http://www.apache.org/licenses/LICENSE-2.0
  //
  // Unless required by applicable law or agreed to in writing, software
  // distributed under the License is distributed on an "AS IS" BASIS,
  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  // See the License for the specific language governing permissions and
  // limitations under the License.
  
  package org.apache.tapestry.util;
  
  import java.util.ArrayList;
  import java.util.HashMap;
  import java.util.List;
  import java.util.Map;
  
  import org.apache.hivemind.ApplicationRuntimeException;
  import org.apache.hivemind.Locatable;
  import org.apache.hivemind.Location;
  import org.apache.hivemind.Resource;
  import org.apache.hivemind.util.ClasspathResource;
  import org.apache.hivemind.util.Defense;
  import org.apache.tapestry.IMarkupWriter;
  import org.apache.tapestry.IRequestCycle;
  import org.apache.tapestry.PageRenderSupport;
  import org.apache.tapestry.Tapestry;
  import org.apache.tapestry.asset.PrivateAsset;
  import org.apache.tapestry.engine.IEngineService;
  
  /**
   * Implementation of {@link org.apache.tapestry.PageRenderSupport}. The
   * {@link org.apache.tapestry.html.Body}&nbsp;component uses an instance of this class.
   * 
   * @author Howard M. Lewis Ship
   * @since 3.1
   */
  public class PageRenderSupportImpl implements Locatable, PageRenderSupport
  {
      private final IEngineService _assetService;
  
      private final Location _location;
  
      // Lines that belong inside the onLoad event handler for the <body> tag.
      private StringBuffer _initializationScript;
  
      // Any other scripting desired
  
      private StringBuffer _bodyScript;
  
      // Contains text lines related to image initializations
  
      private StringBuffer _imageInitializations;
  
      /**
       * Map of URLs to Strings (preloaded image references).
       */
  
      private Map _imageMap;
  
      /**
       * List of included scripts. Values are Strings.
       * 
       * @since 1.0.5
       */
  
      private List _externalScripts;
  
      private IdAllocator _idAllocator;
  
      public PageRenderSupportImpl(IEngineService assetService, Location location)
      {
          Defense.notNull(assetService, "assetService");
  
          _assetService = assetService;
          _location = location;
      }
  
      /**
       * Returns the location, which may be used in error messages. In practical terms, this is the
       * location of the {@link org.apache.tapestry.html.Body}&nbsp;component.
       */
  
      public Location getLocation()
      {
          return _location;
      }
  
      public String getPreloadedImageReference(String URL)
      {
          if (_imageMap == null)
              _imageMap = new HashMap();
  
          String reference = (String) _imageMap.get(URL);
  
          if (reference == null)
          {
              int count = _imageMap.size();
              String varName = "tapestry_preload[" + count + "]";
              reference = varName + ".src";
  
              if (_imageInitializations == null)
                  _imageInitializations = new StringBuffer();
  
              _imageInitializations.append("  ");
              _imageInitializations.append(varName);
              _imageInitializations.append(" = new Image();\n");
              _imageInitializations.append("  ");
              _imageInitializations.append(reference);
              _imageInitializations.append(" = \"");
              _imageInitializations.append(URL);
              _imageInitializations.append("\";\n");
  
              _imageMap.put(URL, reference);
          }
  
          return reference;
      }
  
      public void addBodyScript(String script)
      {
          if (_bodyScript == null)
              _bodyScript = new StringBuffer(script.length());
  
          _bodyScript.append(script);
      }
  
      public void addInitializationScript(String script)
      {
          if (_initializationScript == null)
              _initializationScript = new StringBuffer(script.length() + 1);
  
          _initializationScript.append(script);
          _initializationScript.append('\n');
      }
  
      public void addExternalScript(Resource scriptLocation)
      {
          if (_externalScripts == null)
              _externalScripts = new ArrayList();
  
          if (_externalScripts.contains(scriptLocation))
              return;
  
          // Alas, this won't give a good Location for the actual problem.
  
          if (!(scriptLocation instanceof ClasspathResource))
              throw new ApplicationRuntimeException(Tapestry.format(
                      "Body.include-classpath-script-only",
                      scriptLocation), this, null, null);
  
          // Record the URL so we don't include it twice.
  
          _externalScripts.add(scriptLocation);
  
      }
  
      public String getUniqueString(String baseValue)
      {
          if (_idAllocator == null)
              _idAllocator = new IdAllocator();
  
          return _idAllocator.allocateId(baseValue);
      }
  
      private void writeExternalScripts(IMarkupWriter writer, IRequestCycle cycle)
      {
          int count = Tapestry.size(_externalScripts);
          for (int i = 0; i < count; i++)
          {
              ClasspathResource scriptLocation = (ClasspathResource) _externalScripts.get(i);
  
              // This is still very awkward! Should move the code inside PrivateAsset somewhere
              // else, so that an asset does not have to be created to to build the URL.
              PrivateAsset asset = new PrivateAsset(scriptLocation, _assetService, null);
              String url = asset.buildURL(cycle);
  
              // Note: important to use begin(), not beginEmpty(), because browser don't
              // interpret <script .../> properly.
  
              writer.begin("script");
              writer.attribute("language", "JavaScript");
              writer.attribute("type", "text/javascript");
              writer.attribute("src", url);
              writer.end();
              writer.println();
          }
      }
  
      /**
       * Writes a single large JavaScript block containing:
       * <ul>
       * <li>Any image initializations (via {@link #getPreloadedImageReference(String)})
       * <li>Any included scripts (via {@link #addExternalScript(Resource)})
       * <li>Any contributions (via {@link #addBodyScript(String)})
       * </ul>
       * 
       * @see #writeInitializationScript(IMarkupWriter)
       */
  
      public void writeBodyScript(IMarkupWriter writer, IRequestCycle cycle)
      {
          if (!Tapestry.isEmpty(_externalScripts))
              writeExternalScripts(writer, cycle);
  
          if (!(any(_bodyScript) || any(_imageInitializations)))
              return;
  
          writer.begin("script");
          writer.attribute("language", "JavaScript");
          writer.attribute("type", "text/javascript");
          writer.printRaw("<!--");
  
          if (any(_imageInitializations))
          {
              writer.printRaw("\n\nvar tapestry_preload = new Array();\n");
              writer.printRaw("if (document.images)\n");
              writer.printRaw("{\n");
              writer.printRaw(_imageInitializations.toString());
              writer.printRaw("}\n");
          }
  
          if (any(_bodyScript))
          {
              writer.printRaw("\n\n");
              writer.printRaw(_bodyScript.toString());
          }
  
          writer.printRaw("\n\n// -->");
          writer.end();
      }
  
      /**
       * Writes any image initializations; this should be invoked at the end of the render, after all
       * the related HTML will have already been streamed to the client and parsed by the web browser.
       * Earlier versions of Tapestry uses a <code>window.onload</code> event handler.
       */
  
      public void writeInitializationScript(IMarkupWriter writer)
      {
          if (!any(_initializationScript))
              return;
  
          writer.begin("script");
          writer.attribute("language", "JavaScript");
          writer.attribute("type", "text/javascript");
          writer.printRaw("<!--\n");
  
          writer.printRaw(_initializationScript.toString());
  
          writer.printRaw("\n// -->");
          writer.end();
      }
  
      private boolean any(StringBuffer buffer)
      {
          return buffer != null && buffer.length() > 0;
      }
  }
  
  

---------------------------------------------------------------------
To unsubscribe, e-mail: tapestry-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: tapestry-dev-help@jakarta.apache.org