Coverage Report - org.apache.tapestry.util.PageRenderSupportImpl
 
Classes in this File Line Coverage Branch Coverage Complexity
PageRenderSupportImpl
0%
0/125
0%
0/76
3.087
 
 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 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  0
     private final Map _requires = new HashMap();
 81  
 
 82  
     private IRequestCycle _cycle;
 83  
 
 84  
     /**
 85  
      * Creates a new instance bound to the specific location.
 86  
      *
 87  
      * @param assetFactory
 88  
      *          Used to generate asset urls.
 89  
      * @param namespace
 90  
      *          Namespace that javascript / portlet related items should be in.
 91  
      * @param location
 92  
      *          Location of what is primarily the {@link org.apache.tapestry.html.Body} component.
 93  
      * @param builder
 94  
      *          The response delegate.
 95  
      *
 96  
      * @deprecated To be removed in 4.1.2 - use the
 97  
      *              new {@link #PageRenderSupportImpl(org.apache.tapestry.asset.AssetFactory, String, org.apache.tapestry.services.ResponseBuilder, org.apache.tapestry.IRequestCycle)}
 98  
      *              constructor instead.
 99  
      */
 100  
     public PageRenderSupportImpl(AssetFactory assetFactory, String namespace,
 101  
                                  Location location, ResponseBuilder builder)
 102  0
     {
 103  0
         Defense.notNull(assetFactory, "assetService");
 104  
 
 105  0
         _assetFactory = assetFactory;
 106  0
         _location = location;
 107  0
         _idAllocator = new IdAllocator(namespace);
 108  0
         _builder = builder;
 109  
 
 110  0
         _preloadName = (namespace.equals("") ? "tapestry." : namespace) + "preload";
 111  0
     }
 112  
 
 113  
     public PageRenderSupportImpl(AssetFactory assetFactory, String namespace,
 114  
                                  ResponseBuilder builder, IRequestCycle cycle)
 115  0
     {
 116  0
         Defense.notNull(assetFactory, "assetService");
 117  
 
 118  0
         _assetFactory = assetFactory;
 119  0
         _idAllocator = new IdAllocator(namespace);
 120  0
         _builder = builder;
 121  0
         _cycle = cycle;
 122  
 
 123  0
         _preloadName = (namespace.equals("") ? "tapestry." : namespace) + "preload";
 124  0
     }
 125  
 
 126  
     /**
 127  
      * Returns the location, which may be used in error messages. In practical terms, this is the
 128  
      * location of the {@link org.apache.tapestry.html.Body}&nbsp;component.
 129  
      */
 130  
 
 131  
     public Location getLocation()
 132  
     {
 133  0
         if (_location != null)
 134  
         {
 135  0
             return _location;
 136  
         }
 137  
 
 138  0
         if (_cycle != null)
 139  
         {
 140  0
             IRender render = _cycle.renderStackPeek();
 141  
 
 142  0
             if (render != null && IComponent.class.isInstance(render))
 143  
             {
 144  0
                 return ((IComponent)render).getLocation();
 145  
             }
 146  
         }
 147  
         
 148  0
         return null;
 149  
     }
 150  
 
 151  
     public String getPreloadedImageReference(String URL)
 152  
     {
 153  0
         return getPreloadedImageReference(null, URL);
 154  
     }
 155  
 
 156  
     public String getPreloadedImageReference(IComponent target, IAsset source)
 157  
     {
 158  0
         return getPreloadedImageReference(target, source.buildURL());
 159  
     }
 160  
 
 161  
     public String getPreloadedImageReference(IComponent target, String URL)
 162  
     {
 163  0
         if (target != null
 164  
             && !_builder.isImageInitializationAllowed(target))
 165  0
             return URL;
 166  
 
 167  0
         if (_imageMap == null)
 168  0
             _imageMap = new HashMap();
 169  
 
 170  0
         String reference = (String) _imageMap.get(URL);
 171  
 
 172  0
         if (reference == null)
 173  
         {
 174  0
             int count = _imageMap.size();
 175  0
             String varName = _preloadName + "[" + count + "]";
 176  0
             reference = varName + ".src";
 177  
 
 178  0
             if (_imageInitializations == null)
 179  0
                 _imageInitializations = new StringBuffer();
 180  
 
 181  0
             _imageInitializations.append("  ");
 182  0
             _imageInitializations.append(varName);
 183  0
             _imageInitializations.append(" = new Image();\n");
 184  0
             _imageInitializations.append("  ");
 185  0
             _imageInitializations.append(reference);
 186  0
             _imageInitializations.append(" = \"");
 187  0
             _imageInitializations.append(URL);
 188  0
             _imageInitializations.append("\";\n");
 189  
 
 190  0
             _imageMap.put(URL, reference);
 191  
         }
 192  
 
 193  0
         return reference;
 194  
     }
 195  
 
 196  
     public void addBodyScript(String script)
 197  
     {
 198  0
         addBodyScript(null, script);
 199  0
     }
 200  
 
 201  
     public void addBodyScript(IComponent target, String script)
 202  
     {
 203  0
         if (!_builder.isBodyScriptAllowed(target))
 204  0
             return;
 205  
 
 206  0
         String val = stripDuplicateIncludes(script);
 207  
 
 208  0
         if (_bodyScript == null)
 209  0
             _bodyScript = new StringBuffer(val.length());
 210  
 
 211  0
         _bodyScript.append("\n").append(val);
 212  0
     }
 213  
 
 214  
     /**
 215  
      * {@inheritDoc}
 216  
      */
 217  
     public boolean isBodyScriptAllowed(IComponent target)
 218  
     {
 219  0
         return _builder.isBodyScriptAllowed(target);
 220  
     }
 221  
 
 222  
     /**
 223  
      * {@inheritDoc}
 224  
      */
 225  
     public boolean isExternalScriptAllowed(IComponent target)
 226  
     {
 227  0
         return _builder.isExternalScriptAllowed(target);
 228  
     }
 229  
 
 230  
     /**
 231  
      * {@inheritDoc}
 232  
      */
 233  
     public boolean isInitializationScriptAllowed(IComponent target)
 234  
     {
 235  0
         return _builder.isInitializationScriptAllowed(target);
 236  
     }
 237  
 
 238  
     public void addInitializationScript(String script)
 239  
     {
 240  0
         addInitializationScript(null, script);
 241  0
     }
 242  
 
 243  
     public void addInitializationScript(IComponent target, String script)
 244  
     {
 245  0
         if (!_builder.isInitializationScriptAllowed(target))
 246  0
             return;
 247  
 
 248  0
         String val = stripDuplicateIncludes(script);
 249  
 
 250  0
         if (_initializationScript == null)
 251  0
             _initializationScript = new StringBuffer(val.length() + 1);
 252  
 
 253  0
         _initializationScript.append("\n").append(val);
 254  0
     }
 255  
 
 256  
     public void addScriptAfterInitialization(IComponent target, String script)
 257  
     {
 258  0
         if (!_builder.isInitializationScriptAllowed(target))
 259  0
             return;
 260  
 
 261  0
         String strippedScript = stripDuplicateIncludes(script);
 262  
 
 263  0
         if (_postInitializationScript == null)
 264  0
             _postInitializationScript = new StringBuffer(strippedScript.length() + 1);
 265  
 
 266  0
         _postInitializationScript.append("\n").append(strippedScript);
 267  0
     }
 268  
 
 269  
     /**
 270  
      * Provides a mechanism to strip out duplicate dojo.require calls made in script
 271  
      * templates in order to reduce amount of redundant javascript written to client.
 272  
      *
 273  
      * @param input The incoming script string to check for requires.
 274  
      * @return The input string stripped of all known dojo.require calls, if any.
 275  
      */
 276  
     String stripDuplicateIncludes(String input)
 277  
     {
 278  0
         String[] lines = StringUtils.splitPreserveAllTokens(input, ';');
 279  
 
 280  0
         if (lines == null || lines.length < 1)
 281  0
             return input;
 282  
 
 283  0
         String ret = input;
 284  
 
 285  0
         for (int i=0; i < lines.length; i++)
 286  
         {
 287  0
             if (lines[i].indexOf("dojo.require") < 0)
 288  0
                 continue;
 289  
 
 290  0
             String line = StringUtils.stripToEmpty(lines[i]);
 291  
 
 292  0
             if (_requires.containsKey(line))
 293  
             {
 294  0
                 ret = StringUtils.replaceOnce(ret, line+";", "");
 295  
             } else
 296  
             {
 297  0
                 _requires.put(line, "t");
 298  
             }
 299  
         }
 300  
 
 301  0
         return StringUtils.stripToEmpty(ret.trim());
 302  
     }
 303  
 
 304  
     public void addExternalScript(Resource scriptLocation)
 305  
     {
 306  0
         addExternalScript(null, scriptLocation);
 307  0
     }
 308  
 
 309  
     public void addExternalScript(IComponent target, Resource scriptLocation)
 310  
     {
 311  0
         if (!_builder.isExternalScriptAllowed(target))
 312  0
             return;
 313  
 
 314  0
         if (_externalScripts == null)
 315  0
             _externalScripts = new ArrayList();
 316  
 
 317  0
         if (_externalScripts.contains(scriptLocation))
 318  0
             return;
 319  
 
 320  
         // Record the Resource so we don't include it twice.
 321  
 
 322  0
         _externalScripts.add(scriptLocation);
 323  0
     }
 324  
 
 325  
     public String getUniqueString(String baseValue)
 326  
     {
 327  0
         return _idAllocator.allocateId(baseValue);
 328  
     }
 329  
 
 330  
     private void writeExternalScripts(IMarkupWriter writer, IRequestCycle cycle)
 331  
     {
 332  0
         int count = Tapestry.size(_externalScripts);
 333  0
         for (int i = 0; i < count; i++)
 334  
         {
 335  0
             Resource scriptLocation = (Resource) _externalScripts.get(i);
 336  
 
 337  0
             IAsset asset = _assetFactory.createAsset(scriptLocation, null);
 338  
 
 339  0
             String url = asset.buildURL();
 340  
 
 341  
             // Note: important to use begin(), not beginEmpty(), because browser don't
 342  
             // interpret <script .../> properly.
 343  
 
 344  0
             _builder.writeExternalScript(writer, url, cycle);
 345  
         }
 346  0
     }
 347  
 
 348  
     /**
 349  
      * Writes a single large JavaScript block containing:
 350  
      * <ul>
 351  
      * <li>Any image initializations (via {@link #getPreloadedImageReference(IComponent, String)}).
 352  
      * <li>Any included scripts (via {@link #addExternalScript(Resource)}).
 353  
      * <li>Any contributions (via {@link #addBodyScript(String)}).
 354  
      * </ul>
 355  
      *
 356  
      * @see #writeInitializationScript(IMarkupWriter)
 357  
      * @param writer
 358  
      *          The markup writer to use.
 359  
      * @param cycle
 360  
      *          The current request.
 361  
      */
 362  
 
 363  
     public void writeBodyScript(IMarkupWriter writer, IRequestCycle cycle)
 364  
     {
 365  0
         if (!Tapestry.isEmpty(_externalScripts))
 366  0
             writeExternalScripts(writer, cycle);
 367  
 
 368  0
         if (!(any(_bodyScript) || any(_imageInitializations)))
 369  0
             return;
 370  
 
 371  0
         _builder.beginBodyScript(writer, cycle);
 372  
 
 373  0
         if (any(_imageInitializations))
 374  
         {
 375  0
             _builder.writeImageInitializations(writer,
 376  
                                                StringUtils.stripToEmpty(_imageInitializations.toString()),
 377  
                                                _preloadName,
 378  
                                                cycle);
 379  
         }
 380  
 
 381  0
         if (any(_bodyScript))
 382  
         {
 383  0
             _builder.writeBodyScript(writer, StringUtils.stripToEmpty(_bodyScript.toString()), cycle);
 384  
         }
 385  
 
 386  0
         _builder.endBodyScript(writer, cycle);
 387  0
     }
 388  
 
 389  
     /**
 390  
      * Writes any image initializations; this should be invoked at the end of the render, after all
 391  
      * the related HTML will have already been streamed to the client and parsed by the web browser.
 392  
      * Earlier versions of Tapestry uses a <code>window.onload</code> event handler.
 393  
      *
 394  
      * @param writer
 395  
      *          The markup writer to use.
 396  
      */
 397  
 
 398  
     public void writeInitializationScript(IMarkupWriter writer)
 399  
     {
 400  0
         if (!any(_initializationScript) && !any(_postInitializationScript))
 401  0
             return;
 402  
 
 403  0
         String script = getContent(_initializationScript) + getContent(_postInitializationScript);
 404  
 
 405  0
         _builder.writeInitializationScript(writer, StringUtils.stripToEmpty(script));
 406  0
     }
 407  
 
 408  
     public static String getContent(StringBuffer buffer)
 409  
     {
 410  0
         if (buffer == null || buffer.length() < 1)
 411  0
             return "";
 412  
         
 413  0
         return buffer.toString();
 414  
     }
 415  
 
 416  
     private boolean any(StringBuffer buffer)
 417  
     {
 418  0
         return buffer != null && buffer.length() > 0;
 419  
     }
 420  
 }