Coverage report

  %line %branch
org.apache.commons.jelly.JellyContext
55% 
84% 

 1  
 /*
 2  
  * Copyright 2002,2004 The Apache Software Foundation.
 3  
  *
 4  
  * Licensed under the Apache License, Version 2.0 (the "License");
 5  
  * you may not use this file except in compliance with the License.
 6  
  * You may obtain a copy of the License at
 7  
  *
 8  
  *      http://www.apache.org/licenses/LICENSE-2.0
 9  
  *
 10  
  * Unless required by applicable law or agreed to in writing, software
 11  
  * distributed under the License is distributed on an "AS IS" BASIS,
 12  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13  
  * See the License for the specific language governing permissions and
 14  
  * limitations under the License.
 15  
  */
 16  
 package org.apache.commons.jelly;
 17  
 
 18  
 import java.io.File;
 19  
 import java.io.InputStream;
 20  
 import java.io.IOException;
 21  
 import java.net.MalformedURLException;
 22  
 import java.net.URL;
 23  
 import java.util.Hashtable;
 24  
 import java.util.Iterator;
 25  
 import java.util.Map;
 26  
 
 27  
 import org.apache.commons.jelly.parser.XMLParser;
 28  
 import org.apache.commons.jelly.util.ClassLoaderUtils;
 29  
 import org.apache.commons.logging.Log;
 30  
 import org.apache.commons.logging.LogFactory;
 31  
 
 32  
 import org.xml.sax.InputSource;
 33  
 import org.xml.sax.SAXException;
 34  
 
 35  
 /**
 36  
   * <p><code>JellyContext</code> represents the Jelly context.</p>
 37  
   *
 38  
   * @author <a href="mailto:jstrachan@apache.org">James Strachan</a>
 39  
   * @version $Revision: 1.10 $
 40  
   */
 41  46
 public class JellyContext {
 42  
 
 43  
     /** The Log to which logging calls will be made. */
 44  46
     private static final Log log = LogFactory.getLog(JellyContext.class);
 45  
 
 46  
     /** Default for inheritance of variables **/
 47  
     private static final boolean DEFAULT_INHERIT = true;
 48  
 
 49  
     /** Default for export of variables **/
 50  
     private static final boolean DEFAULT_EXPORT = false;
 51  
 
 52  
     /** String used to denote a script can't be parsed */
 53  
     private static final String BAD_PARSE = "Could not parse Jelly script";
 54  
 
 55  
     /**
 56  
      * The class loader to use for instantiating application objects.
 57  
      * If not specified, the context class loader, or the class loader
 58  
      * used to load this class itself, is used, based on the value of the
 59  
      * <code>useContextClassLoader</code> variable.
 60  
      */
 61  
     protected ClassLoader classLoader;
 62  
 
 63  
     /**
 64  
      * Do we want to use the Context ClassLoader when loading classes
 65  
      * for instantiating new objects?  Default is <code>false</code>.
 66  
      */
 67  478
     protected boolean useContextClassLoader = false;
 68  
 
 69  
     /** The root URL context (where scripts are located from) */
 70  
     private URL rootURL;
 71  
 
 72  
     /** The current URL context (where relative scripts are located from) */
 73  
     private URL currentURL;
 74  
 
 75  
     /** Tag libraries found so far */
 76  478
     private Map taglibs = new Hashtable();
 77  
 
 78  
     /** synchronized access to the variables in scope */
 79  478
     private Map variables = new Hashtable();
 80  
 
 81  
     /** The parent context */
 82  
     private JellyContext parent;
 83  
 
 84  
     /** Do we inherit variables from parent context? */
 85  478
     private boolean inherit = JellyContext.DEFAULT_INHERIT;
 86  
 
 87  
     /** Do we export our variables to parent context? */
 88  478
     private boolean export  = JellyContext.DEFAULT_EXPORT;
 89  
 
 90  
     /** Should we export tag libraries to our parents context */
 91  478
     private boolean exportLibraries = true;
 92  
 
 93  
     /** Should we cache Tag instances, per thread, to reduce object contruction overhead? */
 94  478
     private boolean cacheTags = false;
 95  
 
 96  
     /**
 97  
      * Create a new context with the currentURL set to the rootURL
 98  
      */
 99  308
     public JellyContext() {
 100  308
         this.currentURL = rootURL;
 101  308
         init();
 102  308
     }
 103  
 
 104  
     /**
 105  
      * Create a new context with the given rootURL
 106  
      * @param rootURL the root URL used in resolving absolute resources i.e. those starting with '/'
 107  
      */
 108  
     public JellyContext(URL rootURL) {
 109  2
         this( rootURL, rootURL );
 110  2
     }
 111  
 
 112  
     /**
 113  
      * Create a new context with the given rootURL and currentURL
 114  
      * @param rootURL the root URL used in resolving absolute resources i.e. those starting with '/'
 115  
      * @param currentURL the root URL used in resolving relative resources
 116  
      */
 117  114
     public JellyContext(URL rootURL, URL currentURL) {
 118  114
         this.rootURL = rootURL;
 119  114
         this.currentURL = currentURL;
 120  114
         init();
 121  114
     }
 122  
 
 123  
 
 124  
     /**
 125  
      * Create a new context with the given parent context.
 126  
      * The parent's rootURL and currentURL are set on the child, and the parent's variables are
 127  
      * available in the child context under the name <code>parentScope</code>.
 128  
      *
 129  
      * @param parent the parent context for the newly created context.
 130  
      */
 131  56
     public JellyContext(JellyContext parent) {
 132  56
         this.parent = parent;
 133  56
         this.rootURL = parent.rootURL;
 134  56
         this.currentURL = parent.currentURL;
 135  56
         this.variables.put("parentScope", parent.variables);
 136  56
         this.cacheTags = parent.cacheTags;
 137  56
         init();
 138  56
     }
 139  
 
 140  
     /**
 141  
      * Create a new context with the given parent context.
 142  
      * The parent's rootURL are set on the child, and the parent's variables are
 143  
      * available in the child context under the name <code>parentScope</code>.
 144  
      *
 145  
      * @param parentJellyContext the parent context for the newly created context.
 146  
      * @param currentURL the root URL used in resolving relative resources
 147  
      */
 148  
     public JellyContext(JellyContext parentJellyContext, URL currentURL) {
 149  0
         this(parentJellyContext);
 150  0
         this.currentURL = currentURL;
 151  0
     }
 152  
 
 153  
     /**
 154  
      * Create a new context with the given parent context.
 155  
      * The parent's variables are available in the child context under the name <code>parentScope</code>.
 156  
      *
 157  
      * @param parentJellyContext the parent context for the newly created context.
 158  
      * @param rootURL the root URL used in resolving absolute resources i.e. those starting with '/'
 159  
      * @param currentURL the root URL used in resolving relative resources
 160  
      */
 161  
     public JellyContext(JellyContext parentJellyContext, URL rootURL, URL currentURL) {
 162  0
         this(parentJellyContext, currentURL);
 163  0
         this.rootURL = rootURL;
 164  0
     }
 165  
 
 166  
     /**
 167  
      * Initialize the context.
 168  
      * This includes adding the context to itself under the name <code>context</code> and
 169  
      * making the System Properties available as <code>systemScope</code>
 170  
      */
 171  
     private void init() {
 172  478
         variables.put("context",this);
 173  
         try {
 174  478
             variables.put("systemScope", System.getProperties() );
 175  478
         } catch (SecurityException e) {
 176  0
             log.debug("security exception accessing system properties", e);
 177  
         }
 178  478
     }
 179  
 
 180  
     /**
 181  
      * @return the parent context for this context
 182  
      */
 183  
     public JellyContext getParent() {
 184  1378
         return parent;
 185  
     }
 186  
 
 187  
     /**
 188  
      * @return the scope of the given name, such as the 'parent' scope.
 189  
      * If Jelly is used in a Servlet situation then 'request', 'session' and 'application' are other names
 190  
      * for scopes
 191  
      */
 192  
     public JellyContext getScope(String name) {
 193  0
         if ( "parent".equals( name ) ) {
 194  0
             return getParent();
 195  
         }
 196  0
         return null;
 197  
     }
 198  
 
 199  
     /**
 200  
      * Finds the variable value of the given name in this context or in any other parent context.
 201  
      * If this context does not contain the variable, then its parent is used and then its parent
 202  
      * and so forth until the context with no parent is found.
 203  
      *
 204  
      * @return the value of the variable in this or one of its descendant contexts or null
 205  
      *  if the variable could not be found.
 206  
      */
 207  
     public Object findVariable(String name) {
 208  0
         Object answer = variables.get(name);
 209  0
         boolean definedHere = answer != null || variables.containsKey(name);
 210  
 
 211  0
         if (definedHere) return answer;
 212  
 
 213  0
         if ( answer == null && parent != class="keyword">null ) {
 214  0
             answer = parent.findVariable(name);
 215  
         }
 216  
         // ### this is a hack - remove this when we have support for pluggable Scopes
 217  0
         if ( answer == null ) {
 218  0
             answer = getSystemProperty(name);
 219  
         }
 220  
 
 221  0
         if (log.isDebugEnabled()) {
 222  0
             log.debug("findVariable: " + name + " value: " + answer );
 223  
         }
 224  0
         return answer;
 225  
     }
 226  
 
 227  
 
 228  
     /** @return the value of the given variable name */
 229  
     public Object getVariable(String name) {
 230  2562
         Object value = variables.get(name);
 231  2562
         boolean definedHere = value != null || variables.containsKey(name);
 232  
 
 233  2562
         if (definedHere) return value;
 234  
 
 235  1380
         if ( value == null && isInherit() ) {
 236  1372
             JellyContext parentContext = getParent();
 237  1372
             if (parentContext != null) {
 238  60
                 value = parentContext.getVariable( name );
 239  
             }
 240  
         }
 241  
 
 242  
         // ### this is a hack - remove this when we have support for pluggable Scopes
 243  1380
         if ( value == null ) {
 244  1374
             value = getSystemProperty(name);
 245  
         }
 246  
 
 247  1380
         return value;
 248  
     }
 249  
 
 250  
     /**
 251  
      * Get a system property and handle security exceptions
 252  
      * @param name the name of the property to retrieve
 253  
      * @return the value of the property, or null if a SecurityException occurs
 254  
      */
 255  
     private Object getSystemProperty(String name) {
 256  
         try {
 257  1374
             return System.getProperty(name);
 258  
         }
 259  
         catch (SecurityException e) {
 260  0
             log.debug("security exception accessing system properties", e);
 261  
         }
 262  0
         return null;
 263  
     }
 264  
 
 265  
     /**
 266  
      * @return the value of the given variable name in the given variable scope
 267  
      * @param name is the name of the variable
 268  
      * @param scopeName is the optional scope name such as 'parent'. For servlet environments
 269  
      * this could be 'application', 'session' or 'request'.
 270  
      */
 271  
     public Object getVariable(String name, String scopeName) {
 272  0
         JellyContext scope = getScope(scopeName);
 273  0
         if ( scope != null ) {
 274  0
             return scope.getVariable(name);
 275  
         }
 276  0
         return null;
 277  
     }
 278  
 
 279  
 
 280  
 
 281  
     /** Sets the value of the named variable */
 282  
     public void setVariable(String name, Object value) {
 283  944
         if ( isExport() ) {
 284  6
             getParent().setVariable( name, value );
 285  6
             return;
 286  
         }
 287  938
         if (value == null) {
 288  8
             variables.remove(name);
 289  
         }
 290  
         else {
 291  930
             variables.put(name, value);
 292  
         }
 293  938
     }
 294  
 
 295  
     /**
 296  
      * Sets the value of the given variable name in the given variable scope
 297  
      * @param name is the name of the variable
 298  
      * @param scopeName is the optional scope name such as 'parent'. For servlet environments
 299  
      *  this could be 'application', 'session' or 'request'.
 300  
      * @param value is the value of the attribute
 301  
      */
 302  
     public void setVariable(String name, String scopeName, Object value) {
 303  0
         JellyContext scope = getScope(scopeName);
 304  0
         if ( scope != null ) {
 305  0
             scope.setVariable(name, value);
 306  
         }
 307  0
     }
 308  
 
 309  
     /** Removes the given variable */
 310  
     public void removeVariable(String name) {
 311  12
         variables.remove(name);
 312  12
     }
 313  
 
 314  
     /**
 315  
      * Removes the given variable in the specified scope.
 316  
      *
 317  
      * @param name is the name of the variable
 318  
      * @param scopeName is the optional scope name such as 'parent'. For servlet environments
 319  
      *  this could be 'application', 'session' or 'request'.
 320  
      */
 321  
     public void removeVariable(String name, String scopeName) {
 322  0
         JellyContext scope = getScope(scopeName);
 323  0
         if ( scope != null ) {
 324  0
             scope.removeVariable(name);
 325  
         }
 326  0
     }
 327  
 
 328  
     /**
 329  
      * @return an Iterator over the current variable names in this
 330  
      * context
 331  
      */
 332  
     public Iterator getVariableNames() {
 333  0
         return variables.keySet().iterator();
 334  
     }
 335  
 
 336  
     /**
 337  
      * @return the Map of variables in this scope
 338  
      */
 339  
     public Map getVariables() {
 340  0
         return variables;
 341  
     }
 342  
 
 343  
     /**
 344  
      * Sets the Map of variables to use
 345  
      */
 346  
     public void setVariables(Map variables) {
 347  
         // I have seen this fail when the passed Map contains a key, value
 348  
         // pair where the value is null
 349  4
         for (Iterator iter = variables.entrySet().iterator(); iter.hasNext();) {
 350  0
             Map.Entry element = (Map.Entry) iter.next();
 351  0
             if (element.getValue() != null) {
 352  0
                 this.variables.put(element.getKey(), element.getValue());
 353  
             }
 354  
         }
 355  
         //this.variables.putAll( variables );
 356  2
     }
 357  
 
 358  
     /**
 359  
      * A factory method to create a new child context of the
 360  
      * current context.
 361  
      */
 362  
     public JellyContext newJellyContext(Map newVariables) {
 363  
         // XXXX: should allow this new context to
 364  
         // XXXX: inherit parent contexts?
 365  
         // XXXX: Or at least publish the parent scope
 366  
         // XXXX: as a Map in this new variable scope?
 367  0
         newVariables.put("parentScope", variables);
 368  0
         JellyContext answer = createChildContext();
 369  0
         answer.setVariables(newVariables);
 370  0
         return answer;
 371  
     }
 372  
 
 373  
     /**
 374  
      * A factory method to create a new child context of the
 375  
      * current context.
 376  
      */
 377  
     public JellyContext newJellyContext() {
 378  22
         return createChildContext();
 379  
     }
 380  
 
 381  
     /** Registers the given tag library against the given namespace URI.
 382  
      * This should be called before the parser is used.
 383  
      */
 384  
     public void registerTagLibrary(String namespaceURI, TagLibrary taglib) {
 385  8
         if (log.isDebugEnabled()) {
 386  0
             log.debug("Registering tag library to: " + namespaceURI + " taglib: " + taglib);
 387  
         }
 388  8
         taglibs.put(namespaceURI, taglib);
 389  
 
 390  8
         if (isExportLibraries() && parent != null) {
 391  0
             parent.registerTagLibrary( namespaceURI, taglib );
 392  
         }
 393  8
     }
 394  
 
 395  
     /** Registers the given tag library class name against the given namespace URI.
 396  
      * The class will be loaded via the given ClassLoader
 397  
      * This should be called before the parser is used.
 398  
      */
 399  
     public void registerTagLibrary(
 400  
         String namespaceURI,
 401  
         String className) {
 402  
 
 403  4662
         if (log.isDebugEnabled()) {
 404  0
             log.debug("Registering tag library to: " + namespaceURI + " taglib: " + className);
 405  
         }
 406  4662
         taglibs.put(namespaceURI, className);
 407  
 
 408  4662
         if (isExportLibraries() && parent != null) {
 409  0
             parent.registerTagLibrary( namespaceURI, className );
 410  
         }
 411  4662
     }
 412  
 
 413  
     public boolean isTagLibraryRegistered(String namespaceURI) {
 414  5846
         boolean answer = taglibs.containsKey( namespaceURI );
 415  5846
         if (answer) {
 416  592
             return true;
 417  
         }
 418  5254
         else if ( parent != null ) {
 419  592
             return parent.isTagLibraryRegistered(namespaceURI);
 420  
         }
 421  
         else {
 422  4662
             return false;
 423  
         }
 424  
     }
 425  
 
 426  
     /**
 427  
      * @return the TagLibrary for the given namespace URI or null if one could not be found
 428  
      */
 429  
     public TagLibrary getTagLibrary(String namespaceURI) {
 430  
 
 431  
         // use my own mapping first, so that namespaceURIs can
 432  
         // be redefined inside child contexts...
 433  
 
 434  3034
         Object answer = taglibs.get(namespaceURI);
 435  
 
 436  3034
         if ( answer == null && parent != class="keyword">null ) {
 437  64
             answer = parent.getTagLibrary( namespaceURI );
 438  
         }
 439  
 
 440  3034
         if ( answer instanceof TagLibrary ) {
 441  2846
             return (TagLibrary) answer;
 442  
         }
 443  188
         else if ( answer instanceof String ) {
 444  130
             String className = (String) answer;
 445  130
             Class theClass = null;
 446  
             try {
 447  130
                 theClass = getClassLoader().loadClass(className);
 448  128
             }
 449  
             catch (ClassNotFoundException e) {
 450  2
                 log.error("Could not find the class: " + className, e);
 451  
             }
 452  130
             if ( theClass != null ) {
 453  
                 try {
 454  128
                     Object object = theClass.newInstance();
 455  128
                     if (object instanceof TagLibrary) {
 456  128
                         taglibs.put(namespaceURI, object);
 457  128
                         return (TagLibrary) object;
 458  
                     }
 459  
                     else {
 460  0
                         log.error(
 461  
                             "The tag library object mapped to: "
 462  
                                 + namespaceURI
 463  
                                 + " is not a TagLibrary. Object = "
 464  
                                 + object);
 465  
                     }
 466  0
                 }
 467  
                 catch (Exception e) {
 468  0
                     log.error(
 469  
                         "Could not instantiate instance of class: " + className + ". Reason: " + e,
 470  
                         e);
 471  
                 }
 472  
             }
 473  
         }
 474  
 
 475  60
         return null;
 476  
     }
 477  
 
 478  
     /**
 479  
      * Attempts to parse the script from the given uri using the
 480  
      * {@link #getResource} method then returns the compiled script.
 481  
      */
 482  
     public Script compileScript(String uri) throws JellyException {
 483  0
         XMLParser parser = getXMLParser();
 484  0
         parser.setContext(this);
 485  0
         InputStream in = getResourceAsStream(uri);
 486  0
         if (in == null) {
 487  0
             throw new JellyException("Could not find Jelly script: " + uri);
 488  
         }
 489  0
         Script script = null;
 490  
         try {
 491  0
             script = parser.parse(in);
 492  0
         } catch (IOException e) {
 493  0
             throw new JellyException(JellyContext.BAD_PARSE, e);
 494  
         } catch (SAXException e) {
 495  0
             throw new JellyException(JellyContext.BAD_PARSE, e);
 496  
         }
 497  
 
 498  0
         return script.compile();
 499  
     }
 500  
 
 501  
     /**
 502  
      * Attempts to parse the script from the given URL using the
 503  
      * {@link #getResource} method then returns the compiled script.
 504  
      */
 505  
     public Script compileScript(URL url) throws JellyException {
 506  0
         XMLParser parser = getXMLParser();
 507  0
         parser.setContext(this);
 508  
 
 509  0
         Script script = null;
 510  
         try {
 511  0
             script = parser.parse(url.toString());
 512  0
         } catch (IOException e) {
 513  0
             throw new JellyException(JellyContext.BAD_PARSE, e);
 514  
         } catch (SAXException e) {
 515  0
             throw new JellyException(JellyContext.BAD_PARSE, e);
 516  
         }
 517  
 
 518  0
         return script.compile();
 519  
     }
 520  
 
 521  
     /**
 522  
      * Attempts to parse the script from the given InputSource using the
 523  
      * {@link #getResource} method then returns the compiled script.
 524  
      */
 525  
     public Script compileScript(InputSource source) throws JellyException {
 526  18
         XMLParser parser = getXMLParser();
 527  18
         parser.setContext(this);
 528  
 
 529  18
         Script script = null;
 530  
         try {
 531  18
             script = parser.parse(source);
 532  18
         } catch (IOException e) {
 533  0
             throw new JellyException(JellyContext.BAD_PARSE, e);
 534  
         } catch (SAXException e) {
 535  0
             throw new JellyException(JellyContext.BAD_PARSE, e);
 536  
         }
 537  
 
 538  18
         return script.compile();
 539  
     }
 540  
 
 541  
     /**
 542  
      * @return a thread pooled XMLParser to avoid the startup overhead
 543  
      * of the XMLParser
 544  
      */
 545  
     protected XMLParser getXMLParser() {
 546  18
         XMLParser parser = createXMLParser();
 547  18
         return parser;
 548  
     }
 549  
 
 550  
     /**
 551  
      * Factory method to allow JellyContext implementations to overload how an XMLParser
 552  
      * is created - such as to overload what the default ExpressionFactory should be.
 553  
      */
 554  
     protected XMLParser createXMLParser() {
 555  18
         return new XMLParser();
 556  
     }
 557  
 
 558  
     /**
 559  
      * Parses the script from the given File then compiles it and runs it.
 560  
      *
 561  
      * @return the new child context that was used to run the script
 562  
      */
 563  
     public JellyContext runScript(File file, XMLOutput output) throws JellyException {
 564  
         try {
 565  0
             return runScript(file.toURL(), output, JellyContext.DEFAULT_EXPORT,
 566  
                 JellyContext.DEFAULT_INHERIT);
 567  
         } catch (MalformedURLException e) {
 568  0
             throw new JellyException(e.toString());
 569  
         }
 570  
     }
 571  
 
 572  
     /**
 573  
      * Parses the script from the given URL then compiles it and runs it.
 574  
      *
 575  
      * @return the new child context that was used to run the script
 576  
      */
 577  
     public JellyContext runScript(URL url, XMLOutput output) throws JellyException {
 578  2
         return runScript(url, output, JellyContext.DEFAULT_EXPORT,
 579  
             JellyContext.DEFAULT_INHERIT);
 580  
     }
 581  
 
 582  
     /**
 583  
      * Parses the script from the given InputSource then compiles it and runs it.
 584  
      *
 585  
      * @return the new child context that was used to run the script
 586  
      */
 587  
     public JellyContext runScript(InputSource source, XMLOutput output) throws JellyException {
 588  0
         return runScript(source, output, JellyContext.DEFAULT_EXPORT,
 589  
             JellyContext.DEFAULT_INHERIT);
 590  
     }
 591  
 
 592  
     /**
 593  
      * Parses the script from the given uri using the
 594  
      * JellyContext.getResource() API then compiles it and runs it.
 595  
      *
 596  
      * @return the new child context that was used to run the script
 597  
      */
 598  
     public JellyContext runScript(String uri, XMLOutput output) throws JellyException {
 599  0
         URL url = null;
 600  
         try {
 601  0
             url = getResource(uri);
 602  0
         } catch (MalformedURLException e) {
 603  0
             throw new JellyException(e.toString());
 604  
         }
 605  
 
 606  0
         if (url == null) {
 607  0
             throw new JellyException("Could not find Jelly script: " + url);
 608  
         }
 609  0
         return runScript(url, output, JellyContext.DEFAULT_EXPORT,
 610  
             JellyContext.DEFAULT_INHERIT);
 611  
     }
 612  
 
 613  
     /**
 614  
      * Parses the script from the given uri using the
 615  
      * JellyContext.getResource() API then compiles it and runs it.
 616  
      *
 617  
      * @return the new child context that was used to run the script
 618  
      */
 619  
     public JellyContext runScript(String uri, XMLOutput output,
 620  
                           boolean export, class="keyword">boolean inherit) throws JellyException {
 621  16
         URL url = null;
 622  
         try {
 623  16
             url = getResource(uri);
 624  16
         } catch (MalformedURLException e) {
 625  0
             throw new JellyException(e.toString());
 626  
         }
 627  
 
 628  16
         if (url == null) {
 629  0
             throw new JellyException("Could not find Jelly script: " + url);
 630  
         }
 631  
 
 632  16
         return runScript(url, output, export, inherit);
 633  
     }
 634  
 
 635  
     /**
 636  
      * Parses the script from the given file then compiles it and runs it.
 637  
      *
 638  
      * @return the new child context that was used to run the script
 639  
      */
 640  
     public JellyContext runScript(File file, XMLOutput output,
 641  
                           boolean export, class="keyword">boolean inherit) throws JellyException {
 642  
         try {
 643  0
             return runScript(file.toURL(), output, export, inherit);
 644  
         } catch (MalformedURLException e) {
 645  0
             throw new JellyException(e.toString());
 646  
         }
 647  
     }
 648  
 
 649  
     /**
 650  
      * Parses the script from the given URL then compiles it and runs it.
 651  
      *
 652  
      * @return the new child context that was used to run the script
 653  
      */
 654  
     public JellyContext runScript(URL url, XMLOutput output,
 655  
                           boolean export, class="keyword">boolean inherit) throws JellyException {
 656  18
         return runScript(new InputSource(url.toString()), output, export, inherit);
 657  
     }
 658  
 
 659  
     /**
 660  
      * Parses the script from the given InputSource then compiles it and runs it.
 661  
      *
 662  
      * @return the new child context that was used to run the script
 663  
      */
 664  
     public JellyContext runScript(InputSource source, XMLOutput output,
 665  
                           boolean export, class="keyword">boolean inherit) throws JellyException {
 666  18
         Script script = compileScript(source);
 667  
 
 668  18
         URL newJellyContextURL = null;
 669  
         try {
 670  18
             newJellyContextURL = getJellyContextURL(source);
 671  18
         } catch (MalformedURLException e) {
 672  0
             throw new JellyException(e.toString());
 673  
         }
 674  
 
 675  18
         JellyContext newJellyContext = newJellyContext();
 676  18
         newJellyContext.setRootURL( newJellyContextURL );
 677  18
         newJellyContext.setCurrentURL( newJellyContextURL );
 678  18
         newJellyContext.setExport( export );
 679  18
         newJellyContext.setInherit( inherit );
 680  
 
 681  18
         if ( inherit ) {
 682  
             // use the same variable scopes
 683  14
             newJellyContext.variables = this.variables;
 684  
         }
 685  
 
 686  18
         if (log.isDebugEnabled() ) {
 687  0
             log.debug( "About to run script: " + source.getSystemId() );
 688  0
             log.debug( "root context URL: " + newJellyContext.rootURL );
 689  0
             log.debug( "current context URL: " + newJellyContext.currentURL );
 690  
         }
 691  
 
 692  18
         script.run(newJellyContext, output);
 693  
 
 694  18
         return newJellyContext;
 695  
     }
 696  
 
 697  
     /**
 698  
      * Returns a URL for the given resource from the specified path.
 699  
      * If the uri starts with "/" then the path is taken as relative to
 700  
      * the current context root.
 701  
      * If the uri is a well formed URL then it is used.
 702  
      * If the uri is a file that exists and can be read then it is used.
 703  
      * Otherwise the uri is interpreted as relative to the current context (the
 704  
      * location of the current script).
 705  
      */
 706  
     public URL getResource(String uri) throws MalformedURLException {
 707  16
         if (uri.startsWith("/")) {
 708  
             // append this uri to the context root
 709  0
             return createRelativeURL(rootURL, uri.substring(1));
 710  
         }
 711  
         else {
 712  
             try {
 713  16
                 return new URL(uri);
 714  
             }
 715  
             catch (MalformedURLException e) {
 716  
                 // lets try find a relative resource
 717  16
                 try {
 718  16
                     return createRelativeURL(currentURL, uri);
 719  
                 } catch (MalformedURLException e2) {
 720  0
                     throw e;
 721  
                 }
 722  
             }
 723  
         }
 724  
     }
 725  
 
 726  
     /**
 727  
      * Attempts to open an InputStream to the given resource at the specified path.
 728  
      * If the uri starts with "/" then the path is taken as relative to
 729  
      * the current context root. If the uri is a well formed URL then it
 730  
      * is used. Otherwise the uri is interpreted as relative to the current
 731  
      * context (the location of the current script).
 732  
      *
 733  
      * @return null if this resource could not be loaded, otherwise the resources
 734  
      *  input stream is returned.
 735  
      */
 736  
     public InputStream getResourceAsStream(String uri) {
 737  
         try {
 738  0
             URL url = getResource(uri);
 739  0
             return url.openStream();
 740  
         }
 741  
         catch (Exception e) {
 742  0
             if (log.isTraceEnabled()) {
 743  0
                 log.trace(
 744  
                     "Caught exception attempting to open: " + uri + ". Exception: " + e,
 745  
                     e);
 746  
             }
 747  0
             return null;
 748  
         }
 749  
     }
 750  
 
 751  
 
 752  
     // Properties
 753  
     //-------------------------------------------------------------------------
 754  
 
 755  
     /**
 756  
      * @return the current root context URL from which all absolute resource URIs
 757  
      *  will be relative to. For example in a web application the root URL will
 758  
      *  map to the web directory which contains the WEB-INF directory.
 759  
      */
 760  
     public URL getRootURL() {
 761  0
         return rootURL;
 762  
     }
 763  
 
 764  
     /**
 765  
      * Sets the current root context URL from which all absolute resource URIs
 766  
      *  will be relative to. For example in a web application the root URL will
 767  
      *  map to the web directory which contains the WEB-INF directory.
 768  
      */
 769  
     public void setRootURL(URL rootURL) {
 770  18
         this.rootURL = rootURL;
 771  18
     }
 772  
 
 773  
 
 774  
     /**
 775  
      * @return the current URL context of the current script that is executing.
 776  
      *  This URL context is used to deduce relative scripts when relative URIs are
 777  
      *  used in calls to {@link #getResource} to process relative scripts.
 778  
      */
 779  
     public URL getCurrentURL() {
 780  8
         return currentURL;
 781  
     }
 782  
 
 783  
     /**
 784  
      * Sets the current URL context of the current script that is executing.
 785  
      *  This URL context is used to deduce relative scripts when relative URIs are
 786  
      *  used in calls to {@link #getResource} to process relative scripts.
 787  
      */
 788  
     public void setCurrentURL(URL currentURL) {
 789  108
         this.currentURL = currentURL;
 790  108
     }
 791  
 
 792  
     /**
 793  
      * Returns whether caching of Tag instances, per thread, is enabled.
 794  
      * Caching Tags can boost performance, on some JVMs, by reducing the cost of
 795  
      * object construction when running Jelly inside a multi-threaded application server
 796  
      * such as a Servlet engine.
 797  
      *
 798  
      * @return whether caching of Tag instances is enabled.
 799  
      */
 800  
     public boolean isCacheTags() {
 801  1730
         return cacheTags;
 802  
     }
 803  
 
 804  
     /**
 805  
      * Sets whether caching of Tag instances, per thread, is enabled.
 806  
      * Caching Tags can boost performance, on some JVMs, by reducing the cost of
 807  
      * object construction when running Jelly inside a multi-threaded application server
 808  
      * such as a Servlet engine.
 809  
      *
 810  
      * @param cacheTags Whether caching should be enabled or disabled.
 811  
      */
 812  
     public void setCacheTags(boolean cacheTags) {
 813  0
         this.cacheTags = cacheTags;
 814  0
     }
 815  
 
 816  
     /**
 817  
      * Returns whether we export tag libraries to our parents context
 818  
      * @return boolean
 819  
      */
 820  
     public boolean isExportLibraries() {
 821  4670
         return exportLibraries;
 822  
     }
 823  
 
 824  
     /**
 825  
      * Sets whether we export tag libraries to our parents context
 826  
      * @param exportLibraries The exportLibraries to set
 827  
      */
 828  
     public void setExportLibraries(boolean exportLibraries) {
 829  34
         this.exportLibraries = exportLibraries;
 830  34
     }
 831  
 
 832  
 
 833  
     /**
 834  
      * Sets whether we should export variable definitions to our parent context
 835  
      */
 836  
     public void setExport(boolean export) {
 837  52
         this.export = export;
 838  52
     }
 839  
 
 840  
     /**
 841  
      * @return whether we should export variable definitions to our parent context
 842  
      */
 843  
     public boolean isExport() {
 844  944
         return this.export;
 845  
     }
 846  
 
 847  
     /**
 848  
      * Sets whether we should inherit variables from our parent context
 849  
      */
 850  
     public void setInherit(boolean inherit) {
 851  18
         this.inherit = inherit;
 852  18
     }
 853  
 
 854  
     /**
 855  
      * @return whether we should inherit variables from our parent context
 856  
      */
 857  
     public boolean isInherit() {
 858  1380
         return this.inherit;
 859  
     }
 860  
 
 861  
 
 862  
     /**
 863  
      * Return the class loader to be used for instantiating application objects
 864  
      * when required.  This is determined based upon the following rules:
 865  
      * <ul>
 866  
      * <li>The class loader set by <code>setClassLoader()</code>, if any</li>
 867  
      * <li>The thread context class loader, if it exists and the
 868  
      *     <code>useContextClassLoader</code> property is set to true</li>
 869  
      * <li>The class loader used to load the XMLParser class itself.
 870  
      * </ul>
 871  
      */
 872  
     public ClassLoader getClassLoader() {
 873  130
         return ClassLoaderUtils.getClassLoader(classLoader, useContextClassLoader, getClass());
 874  
     }
 875  
 
 876  
     /**
 877  
      * Set the class loader to be used for instantiating application objects
 878  
      * when required.
 879  
      *
 880  
      * @param classLoader The new class loader to use, or <code>null</code>
 881  
      *  to revert to the standard rules
 882  
      */
 883  
     public void setClassLoader(ClassLoader classLoader) {
 884  0
         this.classLoader = classLoader;
 885  0
     }
 886  
 
 887  
     /**
 888  
      * Return the boolean as to whether the context classloader should be used.
 889  
      */
 890  
     public boolean getUseContextClassLoader() {
 891  0
         return useContextClassLoader;
 892  
     }
 893  
 
 894  
     /**
 895  
      * Determine whether to use the Context ClassLoader (the one found by
 896  
      * calling <code>Thread.currentThread().getContextClassLoader()</code>)
 897  
      * to resolve/load classes.  If not
 898  
      * using Context ClassLoader, then the class-loading defaults to
 899  
      * using the calling-class' ClassLoader.
 900  
      *
 901  
      * @param use determines whether to use JellyContext ClassLoader.
 902  
      */
 903  
     public void setUseContextClassLoader(boolean use) {
 904  0
         useContextClassLoader = use;
 905  0
     }
 906  
 
 907  
 
 908  
     // Implementation methods
 909  
     //-------------------------------------------------------------------------
 910  
     /**
 911  
      * @return a new relative URL from the given root and with the addition of the
 912  
      * extra relative URI
 913  
      *
 914  
      * @param rootURL is the root context from which the relative URI will be applied
 915  
      * @param relativeURI is the relative URI (without a leading "/")
 916  
      * @throws MalformedURLException if the URL is invalid.
 917  
      */
 918  
     protected URL createRelativeURL(URL rootURL, String relativeURI)
 919  
         throws MalformedURLException {
 920  16
         URL url = rootURL;
 921  16
         if (url == null) {
 922  0
             File file = new File(System.getProperty("user.dir"));
 923  0
             url = file.toURL();
 924  
         }
 925  16
         String urlText = url.toString() + relativeURI;
 926  16
         if ( log.isDebugEnabled() ) {
 927  0
             log.debug("Attempting to open url: " + urlText);
 928  
         }
 929  16
         return new URL(urlText);
 930  
     }
 931  
 
 932  
     /**
 933  
      * Strips off the name of a script to create a new context URL
 934  
      */
 935  
     protected URL getJellyContextURL(URL url) throws MalformedURLException {
 936  0
         String text = url.toString();
 937  0
         int idx = text.lastIndexOf('/');
 938  0
         text = text.substring(0, idx + 1);
 939  0
         return new URL(text);
 940  
     }
 941  
 
 942  
     /**
 943  
      * Strips off the name of a script to create a new context URL
 944  
      */
 945  
     protected URL getJellyContextURL(InputSource source) throws MalformedURLException {
 946  18
         String text = source.getSystemId();
 947  18
         int idx = text.lastIndexOf('/');
 948  18
         text = text.substring(0, idx + 1);
 949  18
         return new URL(text);
 950  
     }
 951  
 
 952  
     /**
 953  
      * Factory method to create a new child of this context
 954  
      */
 955  
     protected JellyContext createChildContext() {
 956  22
         return new JellyContext(this);
 957  
     }
 958  
 
 959  
     /**
 960  
      * Change the parent context to the one provided
 961  
      * @param context the new parent context
 962  
      */
 963  
     protected void setParent(JellyContext context)
 964  
     {
 965  0
         parent = context;
 966  0
         this.variables.put("parentScope", parent.variables);
 967  
         // need to re-export tag libraries to the new parent
 968  0
         if (isExportLibraries() && parent != null) {
 969  0
             for (Iterator keys = taglibs.keySet().iterator(); keys.hasNext();)
 970  
             {
 971  0
                 String namespaceURI = (String) keys.next();
 972  0
                 Object tagLibOrClassName = taglibs.get(namespaceURI);
 973  0
                 if (tagLibOrClassName instanceof TagLibrary)
 974  
                 {
 975  0
                     parent.registerTagLibrary( namespaceURI, (TagLibrary) tagLibOrClassName );
 976  
                 }
 977  
                 else
 978  
                 {
 979  0
                     parent.registerTagLibrary( namespaceURI, (String) tagLibOrClassName );
 980  
                 }
 981  
             }
 982  
         }
 983  
 
 984  0
     }
 985  
 
 986  
 }

This report is generated by jcoverage, Maven and Maven JCoverage Plugin.