Coverage Report - org.apache.tapestry.util.PageRenderSupportImpl
 
Classes in this File Line Coverage Branch Coverage Complexity
PageRenderSupportImpl
86% 
92% 
2.545
 
 1  
 // Copyright 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.util;
 16  
 
 17  
 import org.apache.commons.lang.StringUtils;
 18  
 import org.apache.hivemind.Locatable;
 19  
 import org.apache.hivemind.Location;
 20  
 import org.apache.hivemind.Resource;
 21  
 import org.apache.hivemind.util.Defense;
 22  
 import org.apache.tapestry.*;
 23  
 import org.apache.tapestry.asset.AssetFactory;
 24  
 import org.apache.tapestry.services.ResponseBuilder;
 25  
 
 26  
 import java.util.ArrayList;
 27  
 import java.util.HashMap;
 28  
 import java.util.List;
 29  
 import java.util.Map;
 30  
 
 31  
 /**
 32  
  * Implementation of {@link org.apache.tapestry.PageRenderSupport}. The
 33  
  * {@link org.apache.tapestry.html.Body} component uses an instance of this class.
 34  
  *
 35  
  * @author Howard M. Lewis Ship
 36  
  * @since 4.0
 37  
  */
 38  
 public class PageRenderSupportImpl implements Locatable, PageRenderSupport
 39  
 {
 40  
     private final AssetFactory _assetFactory;
 41  
 
 42  
     private final Location _location;
 43  
 
 44  
     private final ResponseBuilder _builder;
 45  
 
 46  
     // Lines that belong inside the onLoad event handler for the <body> tag.
 47  
 
 48  
     private StringBuffer _initializationScript;
 49  
 
 50  
     // Used by addScriptAfterInitialization
 51  
 
 52  
     private StringBuffer _postInitializationScript;
 53  
 
 54  
     // Any other scripting desired
 55  
 
 56  
     private StringBuffer _bodyScript;
 57  
 
 58  
     // Contains text lines related to image initializations
 59  
 
 60  
     private StringBuffer _imageInitializations;
 61  
 
 62  
     /**
 63  
      * Map of URLs to Strings (preloaded image references).
 64  
      */
 65  
 
 66  
     private Map _imageMap;
 67  
 
 68  
     /**
 69  
      * List of included scripts. Values are Strings.
 70  
      *
 71  
      * @since 1.0.5
 72  
      */
 73  
 
 74  
     private List _externalScripts;
 75  
 
 76  
     private final IdAllocator _idAllocator;
 77  
 
 78  
     private final String _preloadName;
 79  
 
 80  9
     private final Map _requires = new HashMap();
 81  
 
 82  
     public PageRenderSupportImpl(AssetFactory assetFactory, String namespace,
 83  
                                  Location location, ResponseBuilder builder)
 84  9
     {
 85  9
         Defense.notNull(assetFactory, "assetService");
 86  
 
 87  9
         _assetFactory = assetFactory;
 88  9
         _location = location;
 89  9
         _idAllocator = new IdAllocator(namespace);
 90  9
         _builder = builder;
 91  
 
 92  9
         _preloadName = (namespace.equals("") ? "tapestry." : namespace) + "preload";
 93  9
     }
 94  
 
 95  
     /**
 96  
      * Returns the location, which may be used in error messages. In practical terms, this is the
 97  
      * location of the {@link org.apache.tapestry.html.Body}&nbsp;component.
 98  
      */
 99  
 
 100  
     public Location getLocation()
 101  
     {
 102  1
         return _location;
 103  
     }
 104  
 
 105  
     public String getPreloadedImageReference(String URL)
 106  
     {
 107  3
         return getPreloadedImageReference(null, URL);
 108  
     }
 109  
 
 110  
     public String getPreloadedImageReference(IComponent target, IAsset source)
 111  
     {
 112  1
         return getPreloadedImageReference(target, source.buildURL());
 113  
     }
 114  
 
 115  
     public String getPreloadedImageReference(IComponent target, String URL)
 116  
     {
 117  4
         if (target != null
 118  
             && !_builder.isImageInitializationAllowed(target))
 119  0
             return URL;
 120  
 
 121  4
         if (_imageMap == null)
 122  2
             _imageMap = new HashMap();
 123  
 
 124  4
         String reference = (String) _imageMap.get(URL);
 125  
 
 126  4
         if (reference == null)
 127  
         {
 128  3
             int count = _imageMap.size();
 129  3
             String varName = _preloadName + "[" + count + "]";
 130  3
             reference = varName + ".src";
 131  
 
 132  3
             if (_imageInitializations == null)
 133  2
                 _imageInitializations = new StringBuffer();
 134  
 
 135  3
             _imageInitializations.append("  ");
 136  3
             _imageInitializations.append(varName);
 137  3
             _imageInitializations.append(" = new Image();\n");
 138  3
             _imageInitializations.append("  ");
 139  3
             _imageInitializations.append(reference);
 140  3
             _imageInitializations.append(" = \"");
 141  3
             _imageInitializations.append(URL);
 142  3
             _imageInitializations.append("\";\n");
 143  
 
 144  3
             _imageMap.put(URL, reference);
 145  
         }
 146  
 
 147  4
         return reference;
 148  
     }
 149  
 
 150  
     public void addBodyScript(String script)
 151  
     {
 152  2
         addBodyScript(null, script);
 153  2
     }
 154  
 
 155  
     public void addBodyScript(IComponent target, String script)
 156  
     {
 157  2
         if (!_builder.isBodyScriptAllowed(target))
 158  0
             return;
 159  
 
 160  2
         String val = stripDuplicateIncludes(script);
 161  
 
 162  2
         if (_bodyScript == null)
 163  2
             _bodyScript = new StringBuffer(val.length());
 164  
 
 165  2
         _bodyScript.append("\n").append(val);
 166  2
     }
 167  
 
 168  
     /**
 169  
      * {@inheritDoc}
 170  
      */
 171  
     public boolean isBodyScriptAllowed(IComponent target)
 172  
     {
 173  0
         return _builder.isBodyScriptAllowed(target);
 174  
     }
 175  
 
 176  
     /**
 177  
      * {@inheritDoc}
 178  
      */
 179  
     public boolean isExternalScriptAllowed(IComponent target)
 180  
     {
 181  0
         return _builder.isExternalScriptAllowed(target);
 182  
     }
 183  
 
 184  
     /**
 185  
      * {@inheritDoc}
 186  
      */
 187  
     public boolean isInitializationScriptAllowed(IComponent target)
 188  
     {
 189  0
         return _builder.isInitializationScriptAllowed(target);
 190  
     }
 191  
 
 192  
     public void addInitializationScript(String script)
 193  
     {
 194  6
         addInitializationScript(null, script);
 195  6
     }
 196  
 
 197  
     public void addInitializationScript(IComponent target, String script)
 198  
     {
 199  6
         if (!_builder.isInitializationScriptAllowed(target))
 200  0
             return;
 201  
 
 202  6
         String val = stripDuplicateIncludes(script);
 203  
 
 204  6
         if (_initializationScript == null)
 205  1
             _initializationScript = new StringBuffer(val.length() + 1);
 206  
 
 207  6
         _initializationScript.append("\n").append(val);
 208  6
     }
 209  
 
 210  
     public void addScriptAfterInitialization(IComponent target, String script)
 211  
     {
 212  0
         if (!_builder.isInitializationScriptAllowed(target))
 213  0
             return;
 214  
 
 215  0
         String strippedScript = stripDuplicateIncludes(script);
 216  
 
 217  0
         if (_postInitializationScript == null)
 218  0
             _postInitializationScript = new StringBuffer(strippedScript.length() + 1);
 219  
 
 220  0
         _postInitializationScript.append("\n").append(strippedScript);
 221  0
     }
 222  
 
 223  
     /**
 224  
      * Provides a mechanism to strip out duplicate dojo.require calls made in script
 225  
      * templates in order to reduce amount of redundant javascript written to client.
 226  
      *
 227  
      * @param input The incoming script string to check for requires.
 228  
      * @return The input string stripped of all known dojo.require calls, if any.
 229  
      */
 230  
     String stripDuplicateIncludes(String input)
 231  
     {
 232  8
         String[] lines = StringUtils.splitPreserveAllTokens(input, ';');
 233  
 
 234  8
         if (lines == null || lines.length < 1)
 235  0
             return input;
 236  
 
 237  8
         String ret = input;
 238  
 
 239  29
         for (int i=0; i < lines.length; i++)
 240  
         {
 241  21
             if (lines[i].indexOf("dojo.require") < 0)
 242  15
                 continue;
 243  
 
 244  6
             String line = StringUtils.stripToEmpty(lines[i]);
 245  
 
 246  6
             if (_requires.containsKey(line))
 247  
             {
 248  3
                 ret = StringUtils.replaceOnce(ret, line+";", "");
 249  
             } else
 250  
             {
 251  3
                 _requires.put(line, "t");
 252  
             }
 253  
         }
 254  
 
 255  8
         return StringUtils.stripToEmpty(ret.trim());
 256  
     }
 257  
 
 258  
     public void addExternalScript(Resource scriptLocation)
 259  
     {
 260  3
         addExternalScript(null, scriptLocation);
 261  3
     }
 262  
 
 263  
     public void addExternalScript(IComponent target, Resource scriptLocation)
 264  
     {
 265  3
         if (!_builder.isExternalScriptAllowed(target))
 266  0
             return;
 267  
 
 268  3
         if (_externalScripts == null)
 269  1
             _externalScripts = new ArrayList();
 270  
 
 271  3
         if (_externalScripts.contains(scriptLocation))
 272  1
             return;
 273  
 
 274  
         // Record the Resource so we don't include it twice.
 275  
 
 276  2
         _externalScripts.add(scriptLocation);
 277  2
     }
 278  
 
 279  
     public String getUniqueString(String baseValue)
 280  
     {
 281  8
         return _idAllocator.allocateId(baseValue);
 282  
     }
 283  
 
 284  
     private void writeExternalScripts(IMarkupWriter writer, IRequestCycle cycle)
 285  
     {
 286  1
         int count = Tapestry.size(_externalScripts);
 287  3
         for (int i = 0; i < count; i++)
 288  
         {
 289  2
             Resource scriptLocation = (Resource) _externalScripts.get(i);
 290  
 
 291  2
             IAsset asset = _assetFactory.createAsset(scriptLocation, null);
 292  
 
 293  2
             String url = asset.buildURL();
 294  
 
 295  
             // Note: important to use begin(), not beginEmpty(), because browser don't
 296  
             // interpret <script .../> properly.
 297  
 
 298  2
             _builder.writeExternalScript(writer, url, cycle);
 299  
         }
 300  1
     }
 301  
 
 302  
     /**
 303  
      * Writes a single large JavaScript block containing:
 304  
      * <ul>
 305  
      * <li>Any image initializations (via {@link #getPreloadedImageReference(IComponent, String)}).
 306  
      * <li>Any included scripts (via {@link #addExternalScript(Resource)}).
 307  
      * <li>Any contributions (via {@link #addBodyScript(String)}).
 308  
      * </ul>
 309  
      *
 310  
      * @see #writeInitializationScript(IMarkupWriter)
 311  
      * @param writer
 312  
      *          The markup writer to use.
 313  
      * @param cycle
 314  
      *          The current request.
 315  
      */
 316  
 
 317  
     public void writeBodyScript(IMarkupWriter writer, IRequestCycle cycle)
 318  
     {
 319  4
         if (!Tapestry.isEmpty(_externalScripts))
 320  1
             writeExternalScripts(writer, cycle);
 321  
 
 322  4
         if (!(any(_bodyScript) || any(_imageInitializations)))
 323  1
             return;
 324  
 
 325  3
         _builder.beginBodyScript(writer, cycle);
 326  
 
 327  3
         if (any(_imageInitializations))
 328  
         {
 329  2
             _builder.writeImageInitializations(writer,
 330  
                                                StringUtils.stripToEmpty(_imageInitializations.toString()),
 331  
                                                _preloadName,
 332  
                                                cycle);
 333  
         }
 334  
 
 335  3
         if (any(_bodyScript))
 336  
         {
 337  2
             _builder.writeBodyScript(writer, StringUtils.stripToEmpty(_bodyScript.toString()), cycle);
 338  
         }
 339  
 
 340  3
         _builder.endBodyScript(writer, cycle);
 341  3
     }
 342  
 
 343  
     /**
 344  
      * Writes any image initializations; this should be invoked at the end of the render, after all
 345  
      * the related HTML will have already been streamed to the client and parsed by the web browser.
 346  
      * Earlier versions of Tapestry uses a <code>window.onload</code> event handler.
 347  
      *
 348  
      * @param writer
 349  
      *          The markup writer to use.
 350  
      */
 351  
 
 352  
     public void writeInitializationScript(IMarkupWriter writer)
 353  
     {
 354  1
         if (!any(_initializationScript) && !any(_postInitializationScript))
 355  0
             return;
 356  
 
 357  1
         String script = getContent(_initializationScript) + getContent(_postInitializationScript);
 358  
 
 359  1
         _builder.writeInitializationScript(writer, StringUtils.stripToEmpty(script));
 360  1
     }
 361  
 
 362  
     public static String getContent(StringBuffer buffer)
 363  
     {
 364  8
         if (buffer == null || buffer.length() < 1)
 365  6
             return "";
 366  
         
 367  2
         return buffer.toString();
 368  
     }
 369  
 
 370  
     private boolean any(StringBuffer buffer)
 371  
     {
 372  13
         return buffer != null && buffer.length() > 0;
 373  
     }
 374  
 }