| Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
| Variable |
|
| 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 |