Coverage Report - org.webmacro.Context
 
Classes in this File Line Coverage Branch Coverage Complexity
Context
34%
49/142
36%
14/38
1.844
Context$TemplateEvaluationContext
100%
1/1
N/A
1.844
 
 1  
 /*
 2  
  * Copyright (C) 1998-2000 Semiotek Inc.  All Rights Reserved.
 3  
  *
 4  
  * Redistribution and use in source and binary forms, with or without
 5  
  * modification, are permitted under the terms of either of the following
 6  
  * Open Source licenses:
 7  
  *
 8  
  * The GNU General Public License, version 2, or any later version, as
 9  
  * published by the Free Software Foundation
 10  
  * (http://www.fsf.org/copyleft/gpl.html);
 11  
  *
 12  
  *  or
 13  
  *
 14  
  * The Semiotek Public License (http://webmacro.org/LICENSE.)
 15  
  *
 16  
  * This software is provided "as is", with NO WARRANTY, not even the
 17  
  * implied warranties of fitness to purpose, or merchantability. You
 18  
  * assume all risks and liabilities associated with its use.
 19  
  *
 20  
  * See www.webmacro.org for more information on the WebMacro project.
 21  
  */
 22  
 
 23  
 
 24  
 package org.webmacro;
 25  
 
 26  
 import java.util.Collection;
 27  
 import java.util.HashMap;
 28  
 import java.util.Map;
 29  
 import java.util.Set;
 30  
 
 31  
 import org.slf4j.Logger;
 32  
 import org.slf4j.LoggerFactory;
 33  
 import org.webmacro.engine.EvaluationExceptionHandler;
 34  
 import org.webmacro.engine.FunctionCall;
 35  
 import org.webmacro.engine.MethodWrapper;
 36  
 
 37  
 /**
 38  
  * A Context contains state. The idea is to put all of the data you
 39  
  * wish to render into the Context and then merge it with a Template
 40  
  * via the Template.write() or Template.evaluate() methods. Actually
 41  
  * you can render any Macro object by passing a Context to its
 42  
  * write() or evaluate() method, not just templates.
 43  
  * <p>
 44  
  * A Context is a per-thread data structure. It should not be shared
 45  
  * between threads since it is not thread safe. The idea is to put all
 46  
  * of the state for a single request into the context and then execute
 47  
  * it, with each request having its own separate context. In this
 48  
  * thread-per-request world view there is no reason to synchronize
 49  
  * the Context objects as they are not shared between threads.
 50  
  * <p>
 51  
  * Ordinarily you acquire a Context object from the WebMacro
 52  
  * interface, use it for awhile, and then recycle() it. But you
 53  
  * can implement your own Context objects and pass it to the
 54  
  * evaluate() and write() method of any Template or other Macro.
 55  
  *
 56  
  * @author Marcel Huijkman
 57  
  *
 58  
  * @version        23-07-2002
 59  
  */
 60  
 public class Context implements Map, Cloneable
 61  
 {
 62  2
     static Logger _log =  LoggerFactory.getLogger(Context.class);
 63  
     
 64  
     private final Broker _broker;
 65  2141
     private HashMap _funcs = null; // lazy initialization
 66  
 
 67  
     private EvaluationExceptionHandler _eeHandler;
 68  
 
 69  2141
     private Map _variables = new HashMap();
 70  
 
 71  2141
     private TemplateEvaluationContext _teContext
 72  
             = new TemplateEvaluationContext();
 73  
 
 74  2
     private static final org.webmacro.engine.UndefinedMacro UNDEF
 75  
             = org.webmacro.engine.UndefinedMacro.getInstance();
 76  
 
 77  
     /**
 78  
      * Create a new Context relative to the default WM instance.
 79  
      */
 80  
     public Context() throws InitException {
 81  0
         this(Broker.getBroker());
 82  0
     }
 83  
 
 84  
     /**
 85  
      * Create a new Context relative to the supplied broker.
 86  
      */
 87  
     public Context (Broker broker)
 88  2141
     {
 89  2141
         _broker = broker;
 90  2141
     }
 91  
 
 92  
     /** Holder for template place. */
 93  2141
     public final static class TemplateEvaluationContext
 94  
     {
 95  
 //      public Block _curBlock;
 96  
 //      public int _curIndex;
 97  
         public String _templateName;
 98  
         public int _lineNo;
 99  
         public int _columnNo;
 100  
     }
 101  
 
 102  
     /**
 103  
      * See cloneContext(). Subclasses should override cloneContext()
 104  
      * rather than the clone() method.
 105  
      */
 106  
     public final Object clone ()
 107  
     {
 108  0
         return cloneContext();
 109  
     }
 110  
 
 111  
     /**
 112  
      * Create a copy of this context. The underlying storage will
 113  
      * be copied and the local variables reset.
 114  
      */
 115  
     public Context cloneContext ()
 116  
     {
 117  
         Context c;
 118  
         try
 119  
         {
 120  0
             c = (Context) super.clone();
 121  
         }
 122  0
         catch (CloneNotSupportedException e)
 123  
         {
 124  0
             e.printStackTrace();
 125  0
             return null; // never going to happen
 126  0
         }
 127  0
         c._teContext = new TemplateEvaluationContext();
 128  0
         if (_variables instanceof HashMap)
 129  
         {
 130  0
             c._variables = (Map) ((HashMap) _variables).clone();
 131  
         }
 132  
         else
 133  
         {
 134  0
             c._variables = new HashMap(_variables);
 135  
         }
 136  0
         return c;
 137  
     }
 138  
 
 139  
     /**
 140  
      * Clear the context so that it can be used for another request.
 141  
      * <p>
 142  
      * Subclasses may override the clear method and add functionality
 143  
      * but they must call super.clear() if they do so.
 144  
      */
 145  
     public void clear ()
 146  
     {
 147  0
         _variables.clear();
 148  0
         _eeHandler = null;
 149  0
     }
 150  
 
 151  
 
 152  
     /**
 153  
      * Get the instance of the Broker for this request.
 154  
      */
 155  
     public final Broker getBroker ()
 156  
     {
 157  2170
         return _broker;
 158  
     }
 159  
 
 160  
     public final TemplateEvaluationContext getTemplateEvaluationContext ()
 161  
     {
 162  57550
         return _teContext;
 163  
     }
 164  
 
 165  
     public final String getCurrentLocation ()
 166  
     {
 167  62
         StringBuffer loc = new StringBuffer();
 168  62
         loc.append(_teContext._templateName == null ? "(unknown)" : _teContext._templateName);
 169  62
         loc.append(":").append(_teContext._lineNo).append(".").append(_teContext._columnNo);
 170  62
         return loc.toString();
 171  
     }
 172  
 
 173  
     /**
 174  
      * Get a log instance that can be used to write log messages
 175  
      * into the log under the supplied log type.
 176  
      */
 177  
     public final Logger getLog (String type, String description)
 178  
     {
 179  0
         return _log;
 180  
     }
 181  
 
 182  
     /**
 183  
      * Get a log instance that can be used to write log messages
 184  
      * into the log under the supplied log type. The type will
 185  
      * be used as the description.
 186  
      */
 187  
     public final Logger getLog (String type)
 188  
     {
 189  4
         return _log;
 190  
     }
 191  
 
 192  
     /**
 193  
      * Get the EvaluationExceptionHandler.
 194  
      */
 195  
     public EvaluationExceptionHandler getEvaluationExceptionHandler ()
 196  
     {
 197  78
         if (_eeHandler != null)
 198  
         {
 199  48
             return _eeHandler;
 200  
         }
 201  
         else
 202  
         {
 203  30
             return _broker.getEvaluationExceptionHandler();
 204  
         }
 205  
     }
 206  
 
 207  
 
 208  
     /**
 209  
      * Set a new EvaluationExceptionHandler.
 210  
      */
 211  
     public void setEvaluationExceptionHandler (EvaluationExceptionHandler eeh)
 212  
     {
 213  2823
         _eeHandler = eeh;
 214  2823
     }
 215  
 
 216  
 
 217  
     /**
 218  
      * Get the named object/property from the Context. If the Object
 219  
      * does not exist and there is a tool of the same name then the
 220  
      * Object will be instantiated and managed by the tool.
 221  
      * If there's no such variable, it throws.
 222  
      */
 223  
     protected Object internalGet (Object name)
 224  
             throws PropertyException
 225  
     {
 226  166694
         Object ret = _variables.get(name);
 227  166694
         if (ret != null || _variables.containsKey(name))
 228  154490
             return ret;
 229  
 
 230  12204
         if (name instanceof String) {
 231  12204
             Object var = _broker.getAutoContextVariable((String) name, this);
 232  12204
             if (var != null) {
 233  1160
                 put(name, var);
 234  1160
                 return var;
 235  
             }
 236  
             else
 237  11044
                 return UNDEF;
 238  
         }
 239  0
         else if (name instanceof FunctionCall)
 240  
         {
 241  0
             FunctionCall fc = (FunctionCall) name;
 242  0
             String fname = fc.getName();
 243  0
             MethodWrapper func = null;
 244  0
             if (_funcs != null) {
 245  0
                 func = (MethodWrapper) _funcs.get(fname);
 246  
             }
 247  0
             if (func == null)
 248  
             {
 249  0
                 func = _broker.getFunction(fname);
 250  
             }
 251  0
             if (func != null)
 252  
             {
 253  0
                 Object[] args = fc.getArguments(this);
 254  0
                 ret = func.invoke(args);
 255  0
             }
 256  
             else
 257  
             {
 258  0
                 _log.error("Function " + fname + " was not loaded!");
 259  
             }
 260  0
             return ret;
 261  
         }
 262  
         else
 263  
         {
 264  
             // changed by Keats 30-Nov-01
 265  0
             return UNDEF;
 266  
         }
 267  
     }
 268  
 
 269  
     /**
 270  
      * Get the named object/property from the Context; returns null if
 271  
      * not found.
 272  
      */
 273  
     public final Object get (Object name)
 274  
     {
 275  
         try
 276  
         {
 277  8
             Object o = internalGet(name);
 278  8
             if (o == UNDEF)
 279  
             {
 280  0
                 return null;
 281  
             }
 282  8
             return o;
 283  
         }
 284  0
         catch (PropertyException e)
 285  
         {
 286  
             // NOTE: I don't think we get here anymore!  -Keats
 287  0
             return null;
 288  
         }
 289  
     }
 290  
 
 291  
     /**
 292  
      * Convenience method for putting static classes into the context, wraps the
 293  
      * class instance in a wrapper.
 294  
      */
 295  
     public final Object put (Object name, Class c)
 296  
     {
 297  0
         if (c == null)
 298  
         {
 299  0
             return _variables.put(name, null);
 300  
         }
 301  
         else
 302  
         {
 303  0
             return _variables.put(name, new org.webmacro.engine.StaticClassWrapper(c));
 304  
         }
 305  
     }
 306  
    
 307  
     //   public final Object put(Object name, MethodWrapper mw){
 308  
     //       System.out.println("Adding function " + name);
 309  
     //       return _funcs.put(name, mw);
 310  
     //   }
 311  
    
 312  
     public final void putFunction (String name, Object instance, String methodName)
 313  
     {
 314  0
         MethodWrapper func = wrapMethod(instance, methodName);
 315  0
         if (_funcs == null)
 316  0
             _funcs = new HashMap();
 317  0
         _funcs.put(name, func);
 318  0
     }
 319  
 
 320  
     public final void putGlobalFunction (String name, Object instance, String methodName)
 321  
     {
 322  0
         MethodWrapper func = wrapMethod(instance, methodName);
 323  0
         _broker.putFunction(name, func);
 324  0
     }
 325  
 
 326  
     private final MethodWrapper wrapMethod (Object instance, String methodName)
 327  
     {
 328  0
         MethodWrapper func = null;
 329  
         try
 330  
         {
 331  0
             func = new MethodWrapper(instance, methodName);
 332  
         }
 333  0
         catch (Exception e)
 334  
         {
 335  0
             String className = null;
 336  0
             if (instance instanceof Class)
 337  
             {
 338  0
                 className = ((Class) instance).getName();
 339  
             }
 340  0
             else if (instance != null)
 341  
             {
 342  0
                 className = instance.getClass().getName();
 343  
             }
 344  0
             _log.error("Unable to construct function from method: "
 345  
                     + methodName + " of class " + className);
 346  0
         }
 347  0
         return func;
 348  
     }
 349  
 
 350  
 
 351  
     /**
 352  
      * Add an object to the context returning the object that was
 353  
      * there previously under the same name, if any.
 354  
      */
 355  
     public final Object put (Object name, Object value)
 356  
     {
 357  35984
         return _variables.put(name, value);
 358  
     }
 359  
 
 360  
     /**
 361  
      * Get the named object from the Context. The name is a list
 362  
      * of property names. The first name is the name of an object
 363  
      * in the context. The subsequent names are properties of
 364  
      * that object which will be searched using introspection.
 365  
      */
 366  
     protected Object internalGet (Object[] names)
 367  
             throws PropertyException
 368  
     {
 369  
         Object instance;
 370  
         try
 371  
         {
 372  106792
             instance = internalGet(names[0]);
 373  
         }
 374  0
         catch (ArrayIndexOutOfBoundsException e)
 375  
         {
 376  0
             throw new PropertyException(
 377  
                     "Attempt to access property with a zero length name array");
 378  106792
         }
 379  106792
         if (names.length == 1)
 380  
         {
 381  0
             return instance;
 382  
         }
 383  106792
         else if (instance == null)
 384  
         {
 385  0
             throw new PropertyException.NullValueException(names[0].toString());
 386  
         }
 387  
         else
 388  
         {
 389  106792
             return _broker._propertyOperators.getProperty(this, instance, names, 1);
 390  
         }
 391  
     }
 392  
 
 393  
     /**
 394  
      * Set the named property in the Context. The first name is
 395  
      * the name of an object in the context. The subsequent names
 396  
      * are properties of that object which will be searched using
 397  
      * introspection.
 398  
      * @return whether or not the set was successful
 399  
      */
 400  
     public final boolean set (Object[] names, Object value)
 401  
             throws PropertyException
 402  
     {
 403  192
         if (names.length == 1)
 404  
         {
 405  0
             put(names[0], value);
 406  0
             return true;
 407  
         }
 408  
         else
 409  
         {
 410  
             Object instance;
 411  
             try
 412  
             {
 413  192
                 instance = internalGet(names[0]);
 414  
             }
 415  0
             catch (ArrayIndexOutOfBoundsException e)
 416  
             {
 417  0
                 return false;
 418  192
             }
 419  192
             return _broker._propertyOperators.setProperty(this, instance, names, 1, value);
 420  
         }
 421  
     }
 422  
 
 423  
     /**
 424  
      * Same as get(name) but can be overridden by subclasses to do
 425  
      * something different.
 426  
      */
 427  
     public Object getProperty (Object name) throws PropertyException
 428  
     {
 429  59702
         return internalGet(name);
 430  
     }
 431  
 
 432  
     /**
 433  
      * Same as put(name,value) but can be overridden by subclasses to do
 434  
      * something different.
 435  
      */
 436  
     public boolean setProperty (Object name, Object value)
 437  
             throws PropertyException
 438  
     {
 439  28206
         put(name, value);
 440  28206
         return true;
 441  
     }
 442  
 
 443  
     /**
 444  
      * Same as get(Object names[]) but can be overridden by subclasses
 445  
      * to behave differently.
 446  
      */
 447  
     public Object getProperty (Object[] names) throws PropertyException
 448  
     {
 449  106792
         return internalGet(names);
 450  
     }
 451  
 
 452  
     /**
 453  
      * Same as set(Object names[], Object value) but can be overridden
 454  
      * by subclasses to behave differently.
 455  
      * @return whether or not the set was successful
 456  
      */
 457  
     public boolean setProperty (Object[] names, Object value)
 458  
             throws PropertyException
 459  
     {
 460  192
         return set(names, value);
 461  
     }
 462  
 
 463  
     /**
 464  
      * Set the underlying Map object. The supplied Map will subsequently
 465  
      * be used to resolve local variables.
 466  
      */
 467  
     public final void setMap (Map m)
 468  
     {
 469  0
         _variables = m;
 470  0
     }
 471  
 
 472  
     /**
 473  
      * Get the underlying Map object.
 474  
      */
 475  
     public final Map getMap ()
 476  
     {
 477  224
         return _variables;
 478  
     }
 479  
 
 480  
     /**
 481  
      * Method from Map interface, operates on underlying Map.
 482  
      */
 483  
     public boolean containsKey (Object key)
 484  
     {
 485  5834
         return _variables.containsKey(key);
 486  
     }
 487  
 
 488  
     /**
 489  
      * Method from Map interface, operates on underlying Map.
 490  
      */
 491  
     public final boolean containsValue (Object value)
 492  
     {
 493  0
         return _variables.containsValue(value);
 494  
     }
 495  
 
 496  
     /**
 497  
      * Method from Map interface, operates on underlying Map.
 498  
      */
 499  
     public final Set entrySet ()
 500  
     {
 501  0
         return _variables.entrySet();
 502  
     }
 503  
 
 504  
     /**
 505  
      * Method from Map interface, operates on underlying Map.
 506  
      */
 507  
     public final boolean isEmpty ()
 508  
     {
 509  0
         return _variables.isEmpty();
 510  
     }
 511  
 
 512  
     /**
 513  
      * Method from Map interface, operates on underlying Map.
 514  
      */
 515  
     public final Set keySet ()
 516  
     {
 517  0
         return _variables.keySet();
 518  
     }
 519  
 
 520  
     /**
 521  
      * Method from Map interface, operates on underlying Map.
 522  
      */
 523  
     public final void putAll (Map t)
 524  
     {
 525  0
         _variables.putAll(t);
 526  0
     }
 527  
 
 528  
     /**
 529  
      * Method from Map interface, operates on underlying Map.
 530  
      */
 531  
     public final Object remove (Object key)
 532  
     {
 533  0
         return _variables.remove(key);
 534  
     }
 535  
 
 536  
     /**
 537  
      * Method from Map interface, operates on underlying Map.
 538  
      */
 539  
     public final int size ()
 540  
     {
 541  0
         return _variables.size();
 542  
     }
 543  
 
 544  
     /**
 545  
      * Method from Map interface, operates on underlying Map.
 546  
      */
 547  
     public final Collection values ()
 548  
     {
 549  0
         return _variables.values();
 550  
     }
 551  
 
 552  
 
 553  
     //////////////////////////////////////////////////////////////
 554  
 
 555  
     /**
 556  
      * Dump the variables (and their values) contained in this Context.  
 557  
      * Output is similiar to
 558  
      * <code>java.util.HashMap.toString()</code>
 559  
      */
 560  
     public String toString ()
 561  
     {
 562  0
         return _variables.toString();
 563  
     }
 564  
 
 565  
     /* Convenience methods for primitive types */
 566  
    
 567  
     public final void put (Object o, int i)
 568  
     {
 569  0
         put(o, new Integer(i));
 570  0
     }
 571  
 
 572  
     public final void put (Object o, byte b)
 573  
     {
 574  0
         put(o, new Byte(b));
 575  0
     }
 576  
 
 577  
     public final void put (Object o, short s)
 578  
     {
 579  0
         put(o, new Short(s));
 580  0
     }
 581  
 
 582  
     public final void put (Object o, long l)
 583  
     {
 584  0
         put(o, new Long(l));
 585  0
     }
 586  
 
 587  
     public final void put (Object o, char c)
 588  
     {
 589  0
         put(o, new Character(c));
 590  0
     }
 591  
 
 592  
     public final void put (Object o, float f)
 593  
     {
 594  0
         put(o, new Float(f));
 595  0
     }
 596  
 
 597  
     public final void put (Object o, double d)
 598  
     {
 599  0
         put(o, new Double(d));
 600  0
     }
 601  
 
 602  
     public final void put (Object o, boolean b)
 603  
     {
 604  0
         put(o, new Boolean(b));
 605  0
     }
 606  
 }