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

Classes in this File | Line Coverage | Branch Coverage | Complexity
IfDirective |
47% | 33/69
25% | 12/48
| 8.5;8.5

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 |  |
     package org.webmacro.directive;
24 |  |
25 |  |
     import java.io.IOException;
26 |  |
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.Block;
33 |  |
     import org.webmacro.engine.BuildContext;
34 |  |
     import org.webmacro.engine.BuildException;
35 |  |
     import org.webmacro.engine.Expression;
36 |  |
37 |  |
     /**
38 |  |
      * IfDirective implements a WebMacro directive for an if..elseif..else
39 |  |
      * control structure.  This directive is more complicated than most others
40 |  |
      * because it has repeating optional subdirectives, and because it tries
41 |  |
      * to do as much constant folding in the build() method as possible.
42 |  |
      * Therefore, the build() method is complicated, but the write() method
43 |  |
      * is fairly simple.
44 |  |
      *
45 |  |
      * Syntax:
46 |  |
      * <pre>
47 |  |
      * #if (condition) { block }
48 |  |
      * [ #elseif (condition) { block } ] *
49 |  |
      * [ #else { block } ]
50 |  |
      * </pre>
51 |  |
      */
52 |  |
53 | 782 |
     class IfDirective extends Directive
54 |  |
     {
55 |  |
56 |  |
         private static final int IF_COND = 1;
57 |  |
         private static final int IF_BLOCK = 2;
58 |  |
         private static final int IF_ELSEIF = 3;
59 |  |
         private static final int IF_ELSE = 4;
60 |  |
         private static final int ELSEIF_COND = 5;
61 |  |
         private static final int ELSEIF_BLOCK = 6;
62 |  |
         private static final int ELSE_BLOCK = 7;
63 |  |
64 |  |
         private int nConditions;
65 |  |
         private Macro[] conditions;
66 |  |
         private Block[] blocks;
67 |  |
         private Block elseBlock;
68 |  |
69 |  |
         private static final ArgDescriptor[]
70 | 2 |
                 elseifArgs = new ArgDescriptor[]{
71 |  |
                     new ConditionArg(ELSEIF_COND),
72 |  |
                     new BlockArg(ELSEIF_BLOCK)
73 |  |
                 },
74 | 2 |
         elseArgs = new ArgDescriptor[]{
75 |  |
             new BlockArg(ELSE_BLOCK)
76 |  |
         },
77 | 2 |
         ifArgs = new ArgDescriptor[]{
78 |  |
             new ConditionArg(IF_COND),
79 |  |
             new BlockArg(IF_BLOCK)
80 |  |
         };
81 |  |
         private static final Subdirective[]
82 | 2 |
                 ifSubdirectives = new Subdirective[]{
83 |  |
                     new OptionalRepeatingSubdirective(IF_ELSEIF, "elseif", elseifArgs,
84 |  |
                             Subdirective.BREAKING),
85 |  |
                     new OptionalSubdirective(IF_ELSE, "else", elseArgs,
86 |  |
                             Subdirective.BREAKING)
87 |  |
                 };
88 |  |
89 |  |
         private static final DirectiveDescriptor
90 | 2 |
                 myDescr = new DirectiveDescriptor("if", null, ifArgs, ifSubdirectives);
91 |  |
92 |  |
93 |  |
         public static DirectiveDescriptor getDescriptor ()
94 |  |
         {
95 | 6 |
             return myDescr;
96 |  |
         }
97 |  |
98 |  |
         public Object build (DirectiveBuilder builder,
99 |  |
                              BuildContext bc)
100 |  |
                 throws BuildException
101 |  |
         {
102 | 782 |
             Object c = builder.getArg(IF_COND, bc);
103 | 782 |
             boolean cMacro = (c instanceof Macro);
104 |  |
             int elseifCount;
105 |  |
             DirectiveArgs elseArgs;
106 | 782 |
             DirectiveArgs[] elseifArgs = null;
107 |  |
108 |  |
             // If condition is static and true -- just return the block (builder)
109 | 782 |
             if (!cMacro && Expression.isTrue(c))
110 | 0 |
                 return builder.getArg(IF_BLOCK, bc);
111 |  |
112 | 782 |
             elseArgs = builder.getSubdirective(IF_ELSE);
113 | 782 |
             elseifArgs = builder.getRepeatingSubdirective(IF_ELSEIF);
114 | 782 |
             elseifCount = (elseifArgs == null) ? 0 : elseifArgs.length;
115 |  |
116 |  |
             // OK, how about no else-if subdirectives?
117 | 782 |
             if (elseifArgs == null)
118 |  |
             {
119 |  |
                 // If condition is static and false -- just return the else block
120 | 782 |
                 if (!cMacro)
121 |  |
                 {
122 |  |
                     // Must be false, since we already tested !cMacro && isTrue(c)
123 | 0 |
                     return (elseArgs != null)
124 |  |
                             ? elseArgs.getArg(ELSE_BLOCK, bc) : "";
125 |  |
                 }
126 |  |
                 else
127 |  |
                 {
128 |  |
                     // Just one condition -- the IF condition, and maybe an ELSE block
129 | 782 |
                     nConditions = 1;
130 | 782 |
                     conditions = new Macro[1];
131 | 782 |
                     blocks = new Block[1];
132 | 782 |
                     conditions[0] = (Macro) c;
133 | 782 |
                     blocks[0] = (Block) builder.getArg(IF_BLOCK, bc);
134 | 782 |
                     if (elseArgs != null)
135 | 336 |
                         elseBlock = (Block) elseArgs.getArg(ELSE_BLOCK, bc);
136 | 782 |
                     return this;
137 |  |
                 }
138 |  |
             }
139 |  |
             else
140 |  |
             {
141 |  |
                 // This is the ugly case -- we have to guess at how many conditions
142 |  |
                 // we'll have.  We start with 1 + count(#elseof), and if any can be
143 |  |
                 // folded out at compile time, we just won't use the whole thing
144 | 0 |
                 int i = 0;
145 | 0 |
                 nConditions = elseifCount + (cMacro ? 1 : 0);
146 | 0 |
                 conditions = new Macro[nConditions];
147 | 0 |
                 blocks = new Block[nConditions];
148 |  |
                 // If we're here, !cMacro -> the condition is false
149 | 0 |
                 if (cMacro)
150 |  |
                 {
151 | 0 |
                     conditions[0] = (Macro) c;
152 | 0 |
                     blocks[0] = (Block) builder.getArg(IF_BLOCK, bc);
153 | 0 |
                     ++i;
154 |  |
                 }
155 | 0 |
                 for (int j = 0; j < elseifCount; j++)
156 |  |
                 {
157 | 0 |
                     c = elseifArgs[j].getArg(ELSEIF_COND, bc);
158 | 0 |
                     if (c instanceof Macro)
159 |  |
                     {
160 | 0 |
                         conditions[i] = (Macro) c;
161 | 0 |
                         blocks[i] = (Block) elseifArgs[j].getArg(ELSEIF_BLOCK, bc);
162 | 0 |
                         ++i;
163 |  |
                     }
164 | 0 |
                     else if (Expression.isTrue(c))
165 |  |
                     {
166 |  |
                         // If all the previous got folded out as false, then just return the
167 |  |
                         // block from this condition, otherwise stash it in the elseBlock
168 |  |
                         // and we're done with #elseif directives
169 | 0 |
                         if (i == 0)
170 | 0 |
                             return elseifArgs[j].getArg(ELSEIF_BLOCK, bc);
171 |  |
                         else
172 |  |
                         {
173 | 0 |
                             elseBlock = (Block) elseifArgs[j].getArg(ELSEIF_BLOCK, bc);
174 | 0 |
                             break;
175 |  |
                         }
176 |  |
                     }
177 |  |
                     else
178 |  |
                     {
179 |  |
                         // Just skip this #elseif directive
180 |  |
                     }
181 |  |
                 }
182 |  |
                 // If we didn't promote one of the elseif blocks to else, get the else
183 | 0 |
                 if (elseBlock == null && elseArgs != null)
184 |  |
                 {
185 |  |
                     // If there are no valid conditions, just return the else block
186 | 0 |
                     if (i == 0)
187 | 0 |
                         return elseArgs.getArg(ELSE_BLOCK, bc);
188 |  |
                     else
189 | 0 |
                         elseBlock = (Block) elseArgs.getArg(ELSE_BLOCK, bc);
190 |  |
                 }
191 |  |
192 | 0 |
                 if (i < nConditions)
193 |  |
                 {
194 |  |
                     // If we folded out some cases, we would want to resize the arrays,
195 |  |
                     // but since the space doesn't really matter, we'll save time by
196 |  |
                     // just remembering how big they really are.
197 | 0 |
                     nConditions = i;
198 |  |
                 }
199 |  |
             }
200 |  |
201 | 0 |
             return this;
202 |  |
         }
203 |  |
204 |  |
205 |  |
         public void write (FastWriter out, Context context)
206 |  |
                 throws PropertyException, IOException
207 |  |
         {
208 |  |
209 | 70770 |
             for (int i = 0; i < nConditions; i++)
210 |  |
             {
211 | 40254 |
                 boolean b = false;
212 |  |
213 | 40254 |
                 b = Expression.isTrue(conditions[i].evaluate(context));
214 | 40252 |
                 if (b)
215 |  |
                 {
216 | 9736 |
                     blocks[i].write(out, context);
217 | 9730 |
                     return;
218 |  |
                 }
219 |  |
             }
220 |  |
221 |  |
             // If we fell out, we ran out of conditions, try the else block if any
222 | 30516 |
             if (elseBlock != null)
223 | 24796 |
                 elseBlock.write(out, context);
224 | 30516 |
         }
225 |  |
226 |  |
         public void accept (TemplateVisitor v)
227 |  |
         {
228 | 0 |
             v.beginDirective(myDescr.name);
229 | 0 |
             for (int i = 0; i < nConditions; i++)
230 |  |
             {
231 | 0 |
                 v.visitDirectiveArg((i == 0) ? "IfCondition" : "ElseIfCondition",
232 |  |
                         conditions[i]);
233 | 0 |
                 v.visitDirectiveArg((i == 0) ? "IfBlock" : "ElseIfBlock", blocks[i]);
234 |  |
             }
235 | 0 |
             if (elseBlock != null)
236 | 0 |
                 v.visitDirectiveArg("ElseBlock", elseBlock);
237 | 0 |
             v.endDirective();
238 | 0 |
         }
239 |  |
240 |  |
     }
241 |  |