Coverage Report

##### Coverage Report - org.webmacro.engine.Variable

Classes in this File | Line Coverage | Branch Coverage | Complexity
Variable |
62% | 40/64
59% | 19/32
| 2.769230769230769;2.769

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.engine;
25 |  |
26 |  |
     import java.io.IOException;
27 |  |
28 |  |
     import org.webmacro.Context;
29 |  |
     import org.webmacro.FastWriter;
30 |  |
     import org.webmacro.Macro;
31 |  |
     import org.webmacro.PropertyException;
32 |  |
     import org.webmacro.TemplateVisitor;
33 |  |
     import org.webmacro.Visitable;
34 |  |
     import org.webmacro.util.Named;
35 |  |
36 |  |
     // PRIMARY CLASS: Variable
37 |  |
38 |  |
     /**
39 |  |
      * A Variable is a reference into a Propertymap.
40 |  |
      * <p>
41 |  |
      * A variable name contains a list of names separated by dots, for
42 |  |
      * example "$User.Identity.email.address" is the list: User, Identity,
43 |  |
      * email, and address.
44 |  |
      * <p>
45 |  |
      * PLEASE NOTE: Case-sensitivity is enforced. "User" is the the same
46 |  |
      * name as "user".
47 |  |
      * <p>
48 |  |
      * What that means: When a template is interpreted, it is interpreted
49 |  |
      * in terms of data in a hashtable/map called the "context". This is
50 |  |
      * actually a Map of type Map. The context contains all the
51 |  |
      * local variables that have been set, as well as other information
52 |  |
      * that Macros may use to evaluate the request.
53 |  |
      * <p>
54 |  |
      * Variable depends heavily on Property introspection: It is defined
55 |  |
      * as a list of one or more names (separated by dots when written).
56 |  |
      * <p>
57 |  |
      * Those names are references to sub-objects within the context. The
58 |  |
      * Variable instance, when interpreted, will decend through the context
59 |  |
      * following fields, method references, or hash table look-ups based
60 |  |
      * on its names.
61 |  |
      * <p>
62 |  |
      * For example, the variable "$User.Identity.email.address" implies
63 |  |
      * that there is a "User" object under the Map--either it is
64 |  |
      * a field within the map, or the map has a getUser() method, or
65 |  |
      * the User can be obtained by calling Map.get("User").
66 |  |
      * <p>
67 |  |
      * The full expansion of $User.Identity.email.address might be:<pre>
68 |  |
      *
69 |  |
      *    Map.get("User").getIdentity().get("email").address
70 |  |
      *
71 |  |
      * </pre>. Variable (actually the Property class it uses) will figure
72 |  |
      * out how to decend through the object like this until it finds the
73 |  |
      * final reference--which is the "value" of the variable.
74 |  |
      * <p>
75 |  |
      * When searchin for subfields Variable prefers fields over getFoo()
76 |  |
      * methods, and getFoo() over get("Foo").
77 |  |
      *
78 |  |
      */
79 |  |
     public abstract class Variable implements Macro, Visitable
80 |  |
     {
81 |  |
82 |  |
83 |  |
         // null: because in BuildContext.getVariableType() we can just
84 |  |
         // return null from a HashMap for a never before heard of variable
85 |  |
         // to mean that it is a PROPERTY_TYPE. Only this code right here
86 |  |
         // and BuildContext.getVariableType() needs to know that.
87 | 2 |
         final static public Object PROPERTY_TYPE = new Object();
88 | 2 |
         final static public Object LOCAL_TYPE = new Object();
89 |  |
90 |  |
         /**
91 |  |
          * The name of this variable.
92 |  |
          */
93 |  |
         private String _vname;
94 |  |
95 |  |
         /**
96 |  |
          * The name as an array.
97 |  |
          */
98 |  |
         protected Object[] _names;
99 |  |
100 |  |
         /**
101 |  |
          * Create a variable with the supplied name. The elements of the name
102 |  |
          * are either strings, or a method reference.
103 |  |
          */
104 |  |
         Variable (Object names[])
105 | 5834 |
         {
106 | 5834 |
             _names = names;
107 |  |
108 | 5834 |
         }
109 |  |
110 |  |
         /**
111 |  |
          * Return the property names for this variable. These are stringified
112 |  |
          * names corresponding to the names of the variable; if one of the
113 |  |
          * elements of the variable name is a method call then the name of
114 |  |
          * the method is inserted at that point as if it were a property name.
115 |  |
          */
116 |  |
         static final String[] makePropertyNames (Object names[])
117 |  |
         {
118 | 0 |
             String[] sn = new String[names.length];
119 | 0 |
             for (int i = 0; i < sn.length; i++)
120 |  |
             {
121 | 0 |
                 sn[i] = (names[i] instanceof Named) ?
122 |  |
                         ((Named) names[i]).getName() : (String) names[i];
123 |  |
             }
124 | 0 |
             return sn;
125 |  |
         }
126 |  |
127 |  |
         public final String[] getPropertyNames ()
128 |  |
         {
129 | 0 |
             return makePropertyNames(_names);
130 |  |
         }
131 |  |
132 |  |
         /**
133 |  |
          * Like getPropertyNames, but only works if isSimpleName is true.
134 |  |
          */
135 |  |
         public final String getName ()
136 |  |
         {
137 | 0 |
             return (_names[0] instanceof Named) ?
138 |  |
                     ((Named) _names[0]).getName()
139 |  |
                     : (String) _names[0];
140 |  |
         }
141 |  |
142 |  |
         /**
143 |  |
          * Returns true if the Variable describes a simple name (one with only
144 |  |
          * one element).
145 |  |
          */
146 |  |
         public boolean isSimpleName ()
147 |  |
         {
148 | 26 |
             return (_names.length == 1);
149 |  |
         }
150 |  |
151 |  |
         /**
152 |  |
          * Looks in the hashTable (context) for a value keyed to this variables
153 |  |
          * name and returns the value string. If the resulting value is a Macro,
154 |  |
          * recursively call its evaluate method.
155 |  |
          * @return String
156 |  |
          */
157 |  |
         final public Object evaluate (Context context) throws PropertyException
158 |  |
         {
159 |  |
             try
160 |  |
             {
161 | 97452 |
                 Object val = getValue(context);
162 | 97430 |
                 if (val instanceof Macro)
163 |  |
                 {
164 | 11038 |
                     val = ((Macro) val).evaluate(context); // recurse
165 |  |
                 }
166 | 97430 |
                 return val;
167 |  |
             }
168 | 0 |
             catch (NullPointerException e)
169 |  |
             {
170 |  |
                 // May throw
171 | 0 |
                 context.getEvaluationExceptionHandler()
172 |  |
                         .evaluate(this, context,
173 |  |
                                   new PropertyException.NullValueException(getVariableName()));
174 | 0 |
                 return null;
175 |  |
             }
176 | 22 |
             catch (PropertyException e)
177 |  |
             {
178 |  |
                 // May throw
179 | 22 |
                 if (e instanceof PropertyException.UndefinedVariableException)
180 |  |
                 {
181 | 0 |
                     PropertyException.UndefinedVariableException uve = (PropertyException.UndefinedVariableException) e;
182 | 0 |
                     if (_names.length > 1)
183 | 0 |
                         uve.setMessage(
184 |  |
                                 "Attempted to reference a property or method of an undefined variable: $" + _names[0]);
185 |  |
                     else
186 | 0 |
                         uve.setMessage(
187 |  |
                                 "Attempted to evaluate an undefined variable: $" + _names[0]);
188 |  |
                 }
189 | 22 |
                 context.getEvaluationExceptionHandler()
190 |  |
                         .evaluate(this, context, e);
191 | 0 |
                 return null;
192 |  |
             }
193 | 0 |
             catch (Exception e)
194 |  |
             {
195 |  |
                 // May throw
196 | 0 |
                 context.getEvaluationExceptionHandler()
197 |  |
                         .evaluate(this, context,
198 |  |
                                 new PropertyException("Variable: exception evaluating "
199 |  |
                                                       + getVariableName(), e));
200 | 0 |
                 return null;
201 |  |
             }
202 |  |
         }
203 |  |
204 |  |
         /**
205 |  |
          * Look in the hashtable (context) for a value keyed to this variables
206 |  |
          * name and write its value to the stream.
207 |  |
          * @exception PropertyException is required data is missing
208 |  |
          * @exception IOException if could not write to output stream
209 |  |
          */
210 |  |
         final public void write (FastWriter out, Context context)
211 |  |
                 throws PropertyException, IOException
212 |  |
         {
213 |  |
             try
214 |  |
             {
215 | 63978 |
                 Object val = getValue(context);
216 | 63956 |
                 if (val instanceof Macro)
217 | 4 |
                     ((Macro) val).write(out, context);
218 |  |
                 else
219 |  |
                 {
220 | 63952 |
                     if (val != null)
221 |  |
                     {
222 | 63926 |
                         String v = val.toString();
223 | 63926 |
                         if (v != null)
224 | 63926 |
                             out.write(v);
225 |  |
                         else
226 |  |
                         {
227 | 0 |
                             out.write(context.getEvaluationExceptionHandler()
228 |  |
                                     .expand(this, context,
229 |  |
                                             new PropertyException.NullToStringException(getVariableName())));
230 |  |
                         }
231 | 63926 |
                     }
232 |  |
                     else
233 |  |
                     {
234 | 26 |
                         if (isSimpleName())
235 |  |
                         {
236 |  |
                             // user accessed a variable that isn't in the context
237 |  |
                             //     $ObjectNotInContext
238 | 0 |
                             out.write(context.getEvaluationExceptionHandler()
239 |  |
                                     .expand(this, context,
240 |  |
                                             new PropertyException.NoSuchVariableException(getVariableName())));
241 |  |
                         }
242 |  |
                         else
243 |  |
                         {
244 |  |
                             // user accessed a valid property who's value is null
245 | 26 |
                             out.write(context.getEvaluationExceptionHandler()
246 |  |
                                     .expand(this, context,
247 |  |
                                             new PropertyException.NullValueException(getVariableName())));
248 |  |
                         }
249 |  |
                     }
250 |  |
                 }
251 |  |
             }
252 | 26 |
             catch (PropertyException e)
253 |  |
             {
254 | 26 |
                 if (e instanceof PropertyException.UndefinedVariableException)
255 |  |
                 {
256 | 4 |
                     PropertyException.UndefinedVariableException uve = (PropertyException.UndefinedVariableException) e;
257 | 4 |
                     if (_names.length > 1)
258 | 0 |
                         uve.setMessage(
259 |  |
                                 "Attempted to write a property or method value of an undefined variable: $" + _names[0]);
260 |  |
                     else
261 | 4 |
                         uve.setMessage(
262 |  |
                                 "Attempted to write an undefined variable: $" + _names[0]);
263 |  |
                 }
264 | 26 |
                 out.write(context.getEvaluationExceptionHandler()
265 |  |
                         .expand(this, context, e));
266 |  |
             }
267 | 0 |
             catch (Exception e)
268 |  |
             {
269 |  |
                 // something we weren't expecting happened!
270 |  |
                 // I wonder if we would ever get here?  --eric
271 | 0 |
                 out.write(context.getEvaluationExceptionHandler()
272 |  |
                         .expand(this, context, e));
273 | 63962 |
             }
274 | 63962 |
         }
275 |  |
276 |  |
         /**
277 |  |
          * Helper method to construct a String name from a Object[] name.
278 |  |
          */
279 |  |
         final static String makeName (Object[] names)
280 |  |
         {
281 | 12 |
             StringBuffer buf = new StringBuffer();
282 | 32 |
             for (int i = 0; i < names.length; i++)
283 |  |
             {
284 | 20 |
                 if (i != 0)
285 |  |
                 {
286 | 8 |
                     buf.append(".");
287 |  |
                 }
288 | 20 |
                 buf.append(names[i]);
289 |  |
             }
290 | 12 |
             return buf.toString();
291 |  |
         }
292 |  |
293 |  |
         /**
294 |  |
          * The code to get the value represented by the variable from the
295 |  |
          * supplied context.
296 |  |
          */
297 |  |
         public abstract Object getValue (Context context) throws PropertyException;
298 |  |
299 |  |
         /**
300 |  |
          * The code to set the value represented by the variable in the
301 |  |
          * supplied context.
302 |  |
          */
303 |  |
         public abstract void setValue (Context c, Object v) throws PropertyException;
304 |  |
305 |  |
         /**
306 |  |
          * Return the String name of the variable prefixed with a string
307 |  |
          * representing its type. For example local:a.b.c
308 |  |
          */
309 |  |
         public abstract String toString ();
310 |  |
311 |  |
         /**
312 |  |
          * Return the canonical name for this variable.
313 |  |
          */
314 |  |
         public synchronized String getVariableName ()
315 |  |
         {
316 | 38 |
           if (_vname == null) {
317 | 12 |
             _vname = makeName(_names).intern();
318 |  |
           }
319 | 38 |
           return _vname;
320 |  |
         }
321 |  |
322 |  |
         public void accept (TemplateVisitor v)
323 |  |
         {
324 | 0 |
             v.visitVariable(this, _names);
325 | 0 |
         }
326 |  |
327 |  |
     }
328 |  |
329 |  |
330 |  |