Coverage Report

##### Coverage Report - org.webmacro.Context

Classes in this File | Line Coverage | Branch Coverage | Complexity
Context |
34% | 49/142
36% | 14/38
| 1.8444444444444446;1.844
Context$TemplateEvaluationContext |
100% | 1/1
N/A | N/A
| 1.8444444444444446;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 |  |
     }