Clover coverage report - Code Coverage for tapestry release 3.1-alpha-1
Coverage timestamp: Mon Feb 21 2005 09:16:14 EST
file stats: LOC: 382   Methods: 13
NCLOC: 182   Classes: 1
30 day Evaluation Version distributed via the Maven Jar Repository. Clover is not free. You have 30 days to evaluate it. Please visit http://www.thecortex.net/clover to obtain a licensed version of Clover
 
 Source file Conditionals Statements Methods TOTAL
Body.java 73.9% 73.6% 92.3% 75.2%
coverage coverage
 1   
 // Copyright 2004, 2005 The Apache Software Foundation
 2   
 //
 3   
 // Licensed under the Apache License, Version 2.0 (the "License");
 4   
 // you may not use this file except in compliance with the License.
 5   
 // You may obtain a copy of the License at
 6   
 //
 7   
 //     http://www.apache.org/licenses/LICENSE-2.0
 8   
 //
 9   
 // Unless required by applicable law or agreed to in writing, software
 10   
 // distributed under the License is distributed on an "AS IS" BASIS,
 11   
 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 12   
 // See the License for the specific language governing permissions and
 13   
 // limitations under the License.
 14   
 
 15   
 package org.apache.tapestry.html;
 16   
 
 17   
 import java.util.ArrayList;
 18   
 import java.util.HashMap;
 19   
 import java.util.List;
 20   
 import java.util.Map;
 21   
 
 22   
 import org.apache.hivemind.util.ClasspathResource;
 23   
 import org.apache.hivemind.ApplicationRuntimeException;
 24   
 import org.apache.hivemind.Resource;
 25   
 import org.apache.tapestry.AbstractComponent;
 26   
 import org.apache.tapestry.IMarkupWriter;
 27   
 import org.apache.tapestry.IRequestCycle;
 28   
 import org.apache.tapestry.IScriptProcessor;
 29   
 import org.apache.tapestry.Tapestry;
 30   
 import org.apache.tapestry.asset.PrivateAsset;
 31   
 import org.apache.tapestry.util.IdAllocator;
 32   
 
 33   
 /**
 34   
  * The body of a Tapestry page. This is used since it allows components on the page access to an
 35   
  * initialization script (that is written the start, just inside the <body> tag). This is
 36   
  * currently used by {@link Rollover}and {@link Script}components. [ <a
 37   
  * href="../../../../../ComponentReference/Body.html">Component Reference </a>]
 38   
  * 
 39   
  * @author Howard Lewis Ship
 40   
  */
 41   
 
 42   
 public abstract class Body extends AbstractComponent implements IScriptProcessor
 43   
 {
 44   
     // Lines that belong inside the onLoad event handler for the <body> tag.
 45   
     private StringBuffer _initializationScript;
 46   
 
 47   
     // Any other scripting desired
 48   
 
 49   
     private StringBuffer _bodyScript;
 50   
 
 51   
     // Contains text lines related to image initializations
 52   
 
 53   
     private StringBuffer _imageInitializations;
 54   
 
 55   
     /**
 56   
      * Map of URLs to Strings (preloaded image references).
 57   
      */
 58   
 
 59   
     private Map _imageMap;
 60   
 
 61   
     /**
 62   
      * List of included scripts. Values are Strings.
 63   
      * 
 64   
      * @since 1.0.5
 65   
      */
 66   
 
 67   
     private List _externalScripts;
 68   
 
 69   
     private IdAllocator _idAllocator;
 70   
 
 71   
     private static final String ATTRIBUTE_NAME = "org.apache.tapestry.active.Body";
 72   
 
 73   
     /**
 74   
      * Tracks a particular preloaded image.
 75   
      */
 76   
 
 77   
     /**
 78   
      * Adds to the script an initialization for the named variable as an Image(), to the given URL.
 79   
      * <p>
 80   
      * Returns a reference, a string that can be used to represent the preloaded image in a
 81   
      * JavaScript function.
 82   
      * 
 83   
      * @since 1.0.2
 84   
      */
 85   
 
 86  0
     public String getPreloadedImageReference(String URL)
 87   
     {
 88  0
         if (_imageMap == null)
 89  0
             _imageMap = new HashMap();
 90   
 
 91  0
         String reference = (String) _imageMap.get(URL);
 92   
 
 93  0
         if (reference == null)
 94   
         {
 95  0
             int count = _imageMap.size();
 96  0
             String varName = "tapestry_preload[" + count + "]";
 97  0
             reference = varName + ".src";
 98   
 
 99  0
             if (_imageInitializations == null)
 100  0
                 _imageInitializations = new StringBuffer();
 101   
 
 102  0
             _imageInitializations.append("  ");
 103  0
             _imageInitializations.append(varName);
 104  0
             _imageInitializations.append(" = new Image();\n");
 105  0
             _imageInitializations.append("  ");
 106  0
             _imageInitializations.append(reference);
 107  0
             _imageInitializations.append(" = \"");
 108  0
             _imageInitializations.append(URL);
 109  0
             _imageInitializations.append("\";\n");
 110   
 
 111  0
             _imageMap.put(URL, reference);
 112   
         }
 113   
 
 114  0
         return reference;
 115   
     }
 116   
 
 117   
     /**
 118   
      * Adds other initialization, in the form of additional JavaScript code to execute from the
 119   
      * &lt;body&gt;'s <code>onLoad</code> event handler. The caller is responsible for adding a
 120   
      * semicolon (statement terminator). This method will add a newline after the script.
 121   
      */
 122   
 
 123  9
     public void addInitializationScript(String script)
 124   
     {
 125  9
         if (_initializationScript == null)
 126  2
             _initializationScript = new StringBuffer(script.length() + 1);
 127   
 
 128  9
         _initializationScript.append(script);
 129  9
         _initializationScript.append('\n');
 130   
 
 131   
     }
 132   
 
 133   
     /**
 134   
      * Adds additional scripting code to the page. This code will be added to a large block of
 135   
      * scripting code at the top of the page (i.e., the before the &lt;body&gt; tag).
 136   
      * <p>
 137   
      * This is typically used to add some form of JavaScript event handler to a page. For example,
 138   
      * the {@link Rollover}component makes use of this.
 139   
      * <p>
 140   
      * Another way this is invoked is by using the {@link Script}component.
 141   
      * <p>
 142   
      * The string will be added, as-is, within the &lt;script&gt; block generated by this
 143   
      * <code>Body</code> component. The script should <em>not</em> contain HTML comments, those
 144   
      * will be supplied by this Body component.
 145   
      * <p>
 146   
      * A frequent use is to add an initialization function using this method, then cause it to be
 147   
      * executed using {@link #addInitializationScript(String)}.
 148   
      */
 149   
 
 150  7
     public void addBodyScript(String script)
 151   
     {
 152  7
         if (_bodyScript == null)
 153  3
             _bodyScript = new StringBuffer(script.length());
 154   
 
 155  7
         _bodyScript.append(script);
 156   
     }
 157   
 
 158   
     /**
 159   
      * Used to include a script from an outside URL (the scriptLocation is a URL, probably obtained
 160   
      * from an asset. This adds an &lt;script src="..."&gt; tag before the main &lt;script&gt; tag.
 161   
      * The Body component ensures that each URL is included only once.
 162   
      * 
 163   
      * @since 1.0.5
 164   
      */
 165   
 
 166  6
     public void addExternalScript(Resource scriptLocation)
 167   
     {
 168  6
         if (_externalScripts == null)
 169  2
             _externalScripts = new ArrayList();
 170   
 
 171  6
         if (_externalScripts.contains(scriptLocation))
 172  3
             return;
 173   
 
 174   
         // Alas, this won't give a good ILocation for the actual problem.
 175   
 
 176  3
         if (!(scriptLocation instanceof ClasspathResource))
 177  0
             throw new ApplicationRuntimeException(Tapestry.format(
 178   
                     "Body.include-classpath-script-only",
 179   
                     scriptLocation), this, null, null);
 180   
 
 181   
         // Record the URL so we don't include it twice.
 182   
 
 183  3
         _externalScripts.add(scriptLocation);
 184   
     }
 185   
 
 186   
     /**
 187   
      * Writes &lt;script&gt; elements for all the external scripts.
 188   
      */
 189  3
     private void writeExternalScripts(IMarkupWriter writer)
 190   
     {
 191  3
         int count = Tapestry.size(_externalScripts);
 192  3
         for (int i = 0; i < count; i++)
 193   
         {
 194  3
             ClasspathResource scriptLocation = (ClasspathResource) _externalScripts.get(i);
 195   
 
 196   
             // This is still very awkward! Should move the code inside PrivateAsset somewhere
 197   
             // else, so that an asset does not have to be created to to build the URL.
 198  3
             PrivateAsset asset = new PrivateAsset(scriptLocation, null);
 199  3
             String url = asset.buildURL(getPage().getRequestCycle());
 200   
 
 201   
             // Note: important to use begin(), not beginEmpty(), because browser don't
 202   
             // interpret <script .../> properly.
 203   
 
 204  3
             writer.begin("script");
 205  3
             writer.attribute("language", "JavaScript");
 206  3
             writer.attribute("type", "text/javascript");
 207  3
             writer.attribute("src", url);
 208  3
             writer.end();
 209  3
             writer.println();
 210   
         }
 211   
 
 212   
     }
 213   
 
 214   
     /**
 215   
      * Retrieves the <code>Body</code> that was stored into the request cycle. This allows
 216   
      * components wrapped by the <code>Body</code> to locate it and access the services it
 217   
      * provides.
 218   
      */
 219   
 
 220  15
     public static Body get(IRequestCycle cycle)
 221   
     {
 222  15
         return (Body) cycle.getAttribute(ATTRIBUTE_NAME);
 223   
     }
 224   
 
 225  109
     protected void renderComponent(IMarkupWriter writer, IRequestCycle cycle)
 226   
     {
 227  109
         if (cycle.getAttribute(ATTRIBUTE_NAME) != null)
 228  0
             throw new ApplicationRuntimeException(Tapestry.getMessage("Body.may-not-nest"), this,
 229   
                     null, null);
 230   
 
 231  109
         cycle.setAttribute(ATTRIBUTE_NAME, this);
 232   
 
 233  109
         IMarkupWriter nested = writer.getNestedWriter();
 234   
 
 235  109
         renderBody(nested, cycle);
 236   
 
 237   
         // Start the body tag.
 238  96
         writer.println();
 239  96
         writer.begin(getElement());
 240  96
         renderInformalParameters(writer, cycle);
 241   
 
 242  96
         writer.println();
 243   
 
 244   
         // Write the page's scripting. This is included scripts
 245   
         // and dynamic JavaScript.
 246   
 
 247  96
         writeScript(writer);
 248   
 
 249   
         // Close the nested writer, which dumps its buffered content
 250   
         // into its parent.
 251   
 
 252  96
         nested.close();
 253   
 
 254   
         // Any initialization should go at the very end of the document
 255   
         // just before the close body tag. Older version of Tapestry
 256   
         // would create a window.onload event handler, but this is better
 257   
         // (it doesn't have to wait for external images to load).
 258   
 
 259  96
         writeInitializationScript(writer);
 260   
 
 261  96
         writer.end(); // <body>
 262   
     }
 263   
 
 264  109
     protected void cleanupAfterRender(IRequestCycle cycle)
 265   
     {
 266  109
         super.cleanupAfterRender(cycle);
 267   
 
 268  109
         if (_idAllocator != null)
 269  4
             _idAllocator.clear();
 270   
 
 271  109
         if (_imageMap != null)
 272  0
             _imageMap.clear();
 273   
 
 274  109
         if (_externalScripts != null)
 275  3
             _externalScripts.clear();
 276   
 
 277  109
         if (_initializationScript != null)
 278  3
             _initializationScript.setLength(0);
 279   
 
 280  109
         if (_imageInitializations != null)
 281  0
             _imageInitializations.setLength(0);
 282   
 
 283  109
         if (_bodyScript != null)
 284  4
             _bodyScript.setLength(0);
 285   
     }
 286   
 
 287   
     /**
 288   
      * Writes a single large JavaScript block containing:
 289   
      * <ul>
 290   
      * <li>Any image initializations
 291   
      * <li>Any scripting
 292   
      * <li>Any initializations
 293   
      * </ul>
 294   
      * <p>
 295   
      * The script is written into a nested markup writer.
 296   
      * <p>
 297   
      * If there are any other initializations (see {@link #addOtherInitialization(String)}), then a
 298   
      * function to execute them is created.
 299   
      */
 300   
 
 301  96
     private void writeScript(IMarkupWriter writer)
 302   
     {
 303  96
         if (!Tapestry.isEmpty(_externalScripts))
 304  3
             writeExternalScripts(writer);
 305   
 
 306  96
         if (!(any(_bodyScript) || any(_imageInitializations)))
 307  92
             return;
 308   
 
 309  4
         writer.begin("script");
 310  4
         writer.attribute("language", "JavaScript");
 311  4
         writer.attribute("type", "text/javascript");
 312  4
         writer.printRaw("<!--");
 313   
 
 314  4
         if (any(_imageInitializations))
 315   
         {
 316  0
             writer.printRaw("\n\nvar tapestry_preload = new Array();\n");
 317  0
             writer.printRaw("if (document.images)\n");
 318  0
             writer.printRaw("{\n");
 319  0
             writer.printRaw(_imageInitializations.toString());
 320  0
             writer.printRaw("}\n");
 321   
         }
 322   
 
 323  4
         if (any(_bodyScript))
 324   
         {
 325  4
             writer.printRaw("\n\n");
 326  4
             writer.printRaw(_bodyScript.toString());
 327   
         }
 328   
 
 329  4
         writer.printRaw("\n\n// -->");
 330  4
         writer.end();
 331   
     }
 332   
 
 333   
     /** @since 3.1 */
 334  96
     private void writeInitializationScript(IMarkupWriter writer)
 335   
     {
 336  96
         if (!any(_initializationScript))
 337  93
             return;
 338   
 
 339  3
         writer.begin("script");
 340  3
         writer.attribute("language", "JavaScript");
 341  3
         writer.attribute("type", "text/javascript");
 342  3
         writer.printRaw("<!--\n");
 343   
 
 344  3
         writer.printRaw(_initializationScript.toString());
 345   
 
 346  3
         writer.printRaw("\n// -->");
 347  3
         writer.end();
 348   
     }
 349   
 
 350  292
     private boolean any(StringBuffer buffer)
 351   
     {
 352  292
         if (buffer == null)
 353  281
             return false;
 354   
 
 355  11
         return buffer.length() > 0;
 356   
     }
 357   
 
 358   
     public abstract String getElement();
 359   
 
 360   
     public abstract void setElement(String element);
 361   
 
 362   
     /**
 363   
      * Sets the element parameter property to its default, "body".
 364   
      * 
 365   
      * @since 3.0
 366   
      */
 367  75
     protected void finishLoad()
 368   
     {
 369  75
         setElement("body");
 370   
     }
 371   
 
 372   
     /** @since 3.0 */
 373   
 
 374  7
     public String getUniqueString(String baseValue)
 375   
     {
 376  7
         if (_idAllocator == null)
 377  3
             _idAllocator = new IdAllocator();
 378   
 
 379  7
         return _idAllocator.allocateId(baseValue);
 380   
     }
 381   
 
 382   
 }