Coverage Report - org.webmacro.directive.IfDirective
 
Classes in this File Line Coverage Branch Coverage Complexity
IfDirective
78%
54/69
60%
29/48
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  1314
 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  40
             elseifArgs = new ArgDescriptor[]{
 71  
                 new ConditionArg(ELSEIF_COND),
 72  
                 new BlockArg(ELSEIF_BLOCK)
 73  
             },
 74  40
     elseArgs = new ArgDescriptor[]{
 75  
         new BlockArg(ELSE_BLOCK)
 76  
     },
 77  40
     ifArgs = new ArgDescriptor[]{
 78  
         new ConditionArg(IF_COND),
 79  
         new BlockArg(IF_BLOCK)
 80  
     };
 81  
     private static final Subdirective[]
 82  40
             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  40
             myDescr = new DirectiveDescriptor("if", null, ifArgs, ifSubdirectives);
 91  
 
 92  
 
 93  
     public static DirectiveDescriptor getDescriptor ()
 94  
     {
 95  63
         return myDescr;
 96  
     }
 97  
 
 98  
     public Object build (DirectiveBuilder builder,
 99  
                          BuildContext bc)
 100  
             throws BuildException
 101  
     {
 102  1314
         Object c = builder.getArg(IF_COND, bc);
 103  1314
         boolean cMacro = (c instanceof Macro);
 104  
         int elseifCount;
 105  
         DirectiveArgs elseArgs;
 106  1314
         DirectiveArgs[] elseifArgs = null;
 107  
 
 108  
         // If condition is static and true -- just return the block (builder)
 109  1314
         if (!cMacro && Expression.isTrue(c))
 110  89
             return builder.getArg(IF_BLOCK, bc);
 111  
 
 112  1225
         elseArgs = builder.getSubdirective(IF_ELSE);
 113  1225
         elseifArgs = builder.getRepeatingSubdirective(IF_ELSEIF);
 114  1225
         elseifCount = (elseifArgs == null) ? 0 : elseifArgs.length;
 115  
 
 116  
         // OK, how about no else-if subdirectives?
 117  1225
         if (elseifArgs == null)
 118  
         {
 119  
             // If condition is static and false -- just return the else block
 120  1182
             if (!cMacro)
 121  
             {
 122  
                 // Must be false, since we already tested !cMacro && isTrue(c)
 123  1
                 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  1181
                 nConditions = 1;
 130  1181
                 conditions = new Macro[1];
 131  1181
                 blocks = new Block[1];
 132  1181
                 conditions[0] = (Macro) c;
 133  1181
                 blocks[0] = (Block) builder.getArg(IF_BLOCK, bc);
 134  1181
                 if (elseArgs != null)
 135  734
                     elseBlock = (Block) elseArgs.getArg(ELSE_BLOCK, bc);
 136  1181
                 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  43
             int i = 0;
 145  43
             nConditions = elseifCount + (cMacro ? 1 : 0);
 146  43
             conditions = new Macro[nConditions];
 147  43
             blocks = new Block[nConditions];
 148  
             // If we're here, !cMacro -> the condition is false
 149  43
             if (cMacro)
 150  
             {
 151  43
                 conditions[0] = (Macro) c;
 152  43
                 blocks[0] = (Block) builder.getArg(IF_BLOCK, bc);
 153  43
                 ++i;
 154  
             }
 155  86
             for (int j = 0; j < elseifCount; j++)
 156  
             {
 157  43
                 c = elseifArgs[j].getArg(ELSEIF_COND, bc);
 158  43
                 if (c instanceof Macro)
 159  
                 {
 160  43
                     conditions[i] = (Macro) c;
 161  43
                     blocks[i] = (Block) elseifArgs[j].getArg(ELSEIF_BLOCK, bc);
 162  43
                     ++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  43
             if (elseBlock == null && elseArgs != null)
 184  
             {
 185  
                 // If there are no valid conditions, just return the else block
 186  6
                 if (i == 0)
 187  0
                     return elseArgs.getArg(ELSE_BLOCK, bc);
 188  
                 else
 189  6
                     elseBlock = (Block) elseArgs.getArg(ELSE_BLOCK, bc);
 190  
             }
 191  
 
 192  43
             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  43
         return this;
 202  
     }
 203  
 
 204  
 
 205  
     public void write (FastWriter out, Context context)
 206  
             throws PropertyException, IOException
 207  
     {
 208  
 
 209  1996
         for (int i = 0; i < nConditions; i++)
 210  
         {
 211  1219
             boolean b = false;
 212  
 
 213  1219
             b = Expression.isTrue(conditions[i].evaluate(context));
 214  1217
             if (b)
 215  
             {
 216  431
                 blocks[i].write(out, context);
 217  431
                 return;
 218  
             }
 219  
         }
 220  
 
 221  
         // If we fell out, we ran out of conditions, try the else block if any
 222  777
         if (elseBlock != null)
 223  427
             elseBlock.write(out, context);
 224  777
     }
 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