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