##### Coverage Report - org.webmacro.directive.TypeDirective

Classes in this File | Line Coverage | Branch Coverage | Complexity
TypeDirective |
7% | 3/42
0% | 0/20
| 3.142857142857143;3.143

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 |  |
     package org.webmacro.directive;
23 |  |
24 |  |
     import java.io.IOException;
25 |  |
26 |  |
     import org.webmacro.Broker;
27 |  |
     import org.webmacro.Context;
28 |  |
     import org.webmacro.FastWriter;
29 |  |
     import org.webmacro.Macro;
30 |  |
     import org.webmacro.PropertyException;
31 |  |
     import org.webmacro.TemplateVisitor;
32 |  |
     import org.webmacro.engine.BuildContext;
33 |  |
     import org.webmacro.engine.BuildException;
34 |  |
     import org.webmacro.engine.UndefinedMacro;
35 |  |
     import org.webmacro.engine.Variable;
36 |  |
     import org.webmacro.util.Settings;
37 |  |
38 |  |
     /**
39 |  |
      * TypeDirective allows the programmer (or template designer) to ensure
40 |  |
      * objects placed into the <code>Context</code> are of the required class
41 |  |
      * type.
42 |  |
      * <p>
43 |  |
      * Syntax:
44 |  |
      * <pre>
45 |  |
      *    #type [ required ] var-reference quoted-string
46 |  |
      * </pre>
47 |  |
      *
48 |  |
      * Examples:
49 |  |
      * <pre>
50 |  |
      *    $MyName, if it exists in the context, <b>must</b> be a java.lang.String
51 |  |
      *    #type $MyName "java.lang.String"
52 |  |
      *
53 |  |
      *    $Today <b>must</b> exist in the context and <b>must</b> be a java.util.Date
54 |  |
      *    #type required $Today "java.util.Date"
55 |  |
      *
56 |  |
      *    $Addresses, if it exists in the context, <b>must</b> be an org.mycompany.til.Address array
57 |  |
      *    #type $Addresses "org.mycompany.util.Address[]"
58 |  |
      * </pre>
59 |  |
      *
60 |  |
      * TypeDirective simply ensures the class type of the <code>$Variable</code>
61 |  |
      * <code>isAssignableFrom</code> the specified classname.<p>
62 |  |
      *
63 |  |
      * If the variable is not flagged as being <i>required</i>, then its class type
64 |  |
      * is only checked if it exists in the context.  By default, variables are not
65 |  |
      * required to be in the context.<p>
66 |  |
      *
67 |  |
      * Special support for checking that a variable is an object array exists.
68 |  |
      * Simply append matching square brackets to the end of the classname.<p>
69 |  |
      *
70 |  |
      * If a Variable is <b>not</b> of the specified type, TypeDirective will
71 |  |
      * throw a <code>org.webmacro.PropertyException.InvalidTypeException</code>,
72 |  |
      * which one can catch in their servlet code if necessary.<p>
73 |  |
      *
74 |  |
      * TypeDirective is <b>enabled by default</b>, however, it can be disabled
75 |  |
      * via your custom <code>WebMacro.properties</code> file:
76 |  |
      * <pre>
77 |  |
      *    TypeDirective.Enabled = true | false
78 |  |
      * </pre>
79 |  |
      *
80 |  |
      * @author  <a href=mailto:ebr@tcdi.com>Eric B. Ridge</a>
81 |  |
      * @version 1.0
82 |  |
      * @since post 0.97
83 |  |
      */
84 | 0 |
     public class TypeDirective extends Directive
85 |  |
     {
86 |  |
87 |  |
         public static final int TYPE_REQUIRED = 0;
88 |  |
         public static final int TYPE_OBJECT = 1;
89 |  |
         public static final int TYPE_CLASSNAME = 2;
90 |  |
91 | 2 |
         public static final ArgDescriptor[] _args = new ArgDescriptor[]{
92 |  |
             new OptionalGroup(1),
93 |  |
             new KeywordArg(TYPE_REQUIRED, "required"),
94 |  |
             new RValueArg(TYPE_OBJECT),
95 |  |
             new QuotedStringArg(TYPE_CLASSNAME),
96 |  |
         };
97 |  |
98 | 2 |
         public static final DirectiveDescriptor _dd =
99 |  |
                 new DirectiveDescriptor("type", null, _args, null);
100 |  |
101 |  |
         /**
102 |  |
          * Static method required by the WebMacro parser to provide
103 |  |
          * a descriptor about this directive.
104 |  |
          */
105 |  |
         public static DirectiveDescriptor getDescriptor ()
106 |  |
         {
107 | 6 |
             return _dd;
108 |  |
         }
109 |  |
110 |  |
111 |  |
         /** The Context object we need to check the type of. */
112 |  |
         private Variable _object;
113 |  |
114 |  |
         /** The Class instance that _object is requried to be. */
115 |  |
         private Class _class;
116 |  |
117 |  |
         /** Is the Variable required to be in the Context? */
118 |  |
         private boolean _required;
119 |  |
120 |  |
         /**
121 |  |
          * Configure directive for this run and return 'this'.
122 |  |
          */
123 |  |
         public Object build (DirectiveBuilder builder, BuildContext bc) throws BuildException
124 |  |
         {
125 |  |
126 | 0 |
             if (!isEnabled(builder.getName(), bc.getBroker()))
127 | 0 |
                 return null;
128 |  |
129 | 0 |
             String classname = (String) builder.getArg(TYPE_CLASSNAME, bc);
130 | 0 |
             _object = (Variable) builder.getArg(TYPE_OBJECT, bc);
131 | 0 |
             _required = builder.getArg(TYPE_REQUIRED, bc) != null;
132 |  |
133 |  |
             try
134 |  |
             {
135 | 0 |
                 _class = TypeDirective.getClass(classname);
136 |  |
             }
137 | 0 |
             catch (ClassNotFoundException cnfe)
138 |  |
             {
139 | 0 |
                 throw new BuildException("TypeDirective cannot find the class "
140 |  |
                         + "/" + classname + "/");
141 | 0 |
             }
142 |  |
143 | 0 |
             return this;
144 |  |
         }
145 |  |
146 |  |
         /**
147 |  |
          * Ensure the class of the specified Variable reference
148 |  |
          * <code>isAssignableFrom</code> from the classname arg.
149 |  |
          *
150 |  |
          * @return <code>null</code>, always
151 |  |
          * @throws PropertyException.NoSuchVariableException if the
152 |  |
          *         specified variable arg evaluates to null
153 |  |
          */
154 |  |
         public Object evaluate (Context context) throws PropertyException
155 |  |
         {
156 | 0 |
             Object o = _object;
157 |  |
158 |  |
             // evaluate the _object reference down to its base object
159 | 0 |
             while (o instanceof Macro && o != UndefinedMacro.getInstance())
160 | 0 |
                 o = ((Macro) o).evaluate(context);
161 |  |
162 | 0 |
             if (o == null || o == UndefinedMacro.getInstance())
163 |  |
             {
164 |  |
                 // the Variable to check isn't in the Context.
165 | 0 |
                 if (_required)
166 |  |
                 {
167 |  |
                     // but it should be
168 | 0 |
                     throw new PropertyException
169 |  |
                             .NoSuchVariableException(_object.getName());
170 |  |
                 }
171 |  |
                 else
172 |  |
                 {
173 |  |
                     // but it's not required to be there, so get out now
174 |  |
                     // can't check the type of a null object
175 | 0 |
                     return null;
176 |  |
                 }
177 |  |
             }
178 |  |
179 |  |
             // check it and throw if requried class isn't compatible
180 |  |
             // with class of specified object
181 | 0 |
             if (!_class.isAssignableFrom(o.getClass()))
182 | 0 |
                 throw new PropertyException.InvalidTypeException(_object.getName(),
183 |  |
                         _class);
184 |  |
185 | 0 |
             return null;
186 |  |
         }
187 |  |
188 |  |
         /**
189 |  |
          * The #type directive does not produce output.
190 |  |
          */
191 |  |
         public void write (FastWriter fw, Context context) throws IOException, PropertyException
192 |  |
         {
193 | 0 |
             evaluate(context);
194 | 0 |
         }
195 |  |
196 |  |
         public void accept (TemplateVisitor v)
197 |  |
         {
198 | 0 |
             v.beginDirective(_dd.name);
199 | 0 |
             v.visitDirectiveArg("TypeContextObject", _object);
200 | 0 |
             v.visitDirectiveArg("TypeClassname", _class.getName());
201 | 0 |
             v.endDirective();
202 | 0 |
         }
203 |  |
204 |  |
         /**
205 |  |
          * Check the configuration and see if we're enabled or not.  By default, we
206 |  |
          * are <b>enabled</b>, even if the configuration key doesn't exist.
207 |  |
          */
208 |  |
         private final boolean isEnabled (String directiveName, Broker broker)
209 |  |
         {
210 | 0 |
             Settings s = broker.getSettings();
211 | 0 |
             return s.getBooleanSetting(directiveName + ".Enabled", true);
212 |  |
         }
213 |  |
214 |  |
         //
215 |  |
         // private, static methods
216 |  |
         //
217 |  |
218 |  |
         /**
219 |  |
          * Use specified class name to return its Class instance.  special support
220 |  |
          * for an alternate syntax for object arrays:<pre>
221 |  |
          *    java.util.Date[]   -- a 1d array of Date objects
222 |  |
          *    java.util.Data[][] -- a 2d array of Date objects
223 |  |
          * </pre>
224 |  |
          */
225 |  |
         private static final Class getClass (final String classname) throws ClassNotFoundException
226 |  |
         {
227 |  |
             Class clazz;
228 |  |
229 | 0 |
             if (classname.endsWith("[]"))
230 |  |
             {
231 |  |
                 // an object array of some kind
232 | 0 |
                 String newName = "[L" + classname.substring(0, classname.length() - 2);
233 | 0 |
                 if (classname.endsWith("[][]")) // support 2d arrays
234 | 0 |
                     newName = "[" + newName.substring(0, newName.length() - 2);
235 | 0 |
                 newName += ";";
236 |  |
237 | 0 |
                 clazz = Class.forName(newName);
238 | 0 |
             }
239 |  |
             else
240 |  |
             {
241 |  |
                 // a normal object
242 | 0 |
                 clazz = Class.forName(classname);
243 |  |
             }
244 |  |
245 | 0 |
             return clazz;
246 |  |
         }
247 |  |
     }
248 |  |