Coverage Report - org.webmacro.directive.CountDirective
 
Classes in this File Line Coverage Branch Coverage Complexity
CountDirective
76%
61/80
76%
38/50
9.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  
 package org.webmacro.directive;
 23  
 
 24  
 import org.webmacro.Context;
 25  
 import org.webmacro.FastWriter;
 26  
 import org.webmacro.Macro;
 27  
 import org.webmacro.PropertyException;
 28  
 import org.webmacro.engine.Block;
 29  
 import org.webmacro.engine.BuildContext;
 30  
 import org.webmacro.engine.BuildException;
 31  
 import org.webmacro.engine.UndefinedMacro;
 32  
 import org.webmacro.engine.Variable;
 33  
 
 34  
 import java.io.IOException;
 35  
 
 36  
 /**
 37  
  * Syntax:
 38  
  * <pre>
 39  
  * #count $i from 1 to 100 [step 1]
 40  
  * </pre>
 41  
  * 
 42  
  * @author Eric B. Ridge (ebr@tcdi.com)
 43  
  * @since 1.1b1
 44  
  */
 45  10
 public class CountDirective extends Directive
 46  
 {
 47  
 
 48  
     private static final int COUNT_ITERATOR = 1;
 49  
     private static final int COUNT_FROM_K = 2;
 50  
     private static final int COUNT_START = 3;
 51  
     private static final int COUNT_TO_K = 4;
 52  
     private static final int COUNT_END = 5;
 53  
     private static final int COUNT_STEP_K = 6;
 54  
     private static final int COUNT_STEP = 7;
 55  
     private static final int COUNT_BODY = 8;
 56  
 
 57  40
     private static final Directive.ArgDescriptor[] _args = 
 58  
         new Directive.ArgDescriptor[]{
 59  
             new Directive.LValueArg(COUNT_ITERATOR),
 60  
             new Directive.KeywordArg(COUNT_FROM_K, "from"),
 61  
             new Directive.RValueArg(COUNT_START),
 62  
             new Directive.KeywordArg(COUNT_TO_K, "to"),
 63  
             new Directive.RValueArg(COUNT_END),
 64  
             new Directive.OptionalGroup(2),
 65  
             new Directive.KeywordArg(COUNT_STEP_K, "step"),
 66  
             new Directive.RValueArg(COUNT_STEP),
 67  
             new Directive.BlockArg(COUNT_BODY),
 68  
        };
 69  
 
 70  40
     private static final DirectiveDescriptor _desc = 
 71  
         new DirectiveDescriptor("count", null, _args, null);
 72  
 
 73  
     public static DirectiveDescriptor getDescriptor ()
 74  
     {
 75  63
         return _desc;
 76  
     }
 77  
 
 78  
     private Variable _iterator;
 79  
     private Macro _body;
 80  
     private Object _objStart;
 81  
     private Object _objEnd;
 82  
     private Object _objStep;
 83  
 
 84  10
     private int _start, _end, _step = Integer.MAX_VALUE;
 85  
 
 86  
     public Object build (DirectiveBuilder builder, BuildContext bc) 
 87  
         throws BuildException
 88  
     {
 89  
         try
 90  
         {
 91  10
             _iterator = (Variable) builder.getArg(COUNT_ITERATOR, bc);
 92  
         }
 93  0
         catch (ClassCastException e)
 94  
         {
 95  0
             throw new Directive.NotVariableBuildException(_desc.name, e);
 96  10
         }
 97  10
         _objStart = builder.getArg(COUNT_START, bc);
 98  10
         _objEnd = builder.getArg(COUNT_END, bc);
 99  10
         _objStep = builder.getArg(COUNT_STEP, bc);
 100  10
         _body = (Block) builder.getArg(COUNT_BODY, bc);
 101  
 
 102  
         // attempt to go ahead and force the start, end, and step values 
 103  
         // into primitive ints
 104  10
         if (_objStart != null)
 105  
         {
 106  10
             if (_objStart instanceof Number)
 107  
             {
 108  9
                 _start = ((Number) _objStart).intValue();
 109  9
                 _objStart = null;
 110  
             }
 111  1
             else if (_objStart instanceof String)
 112  
             {
 113  0
                 _start = Integer.parseInt((String) _objStart);
 114  0
                 _objStart = null;
 115  
             }
 116  
         }
 117  10
         if (_objEnd != null)
 118  
         {
 119  10
             if (_objEnd instanceof Number)
 120  
             {
 121  9
                 _end = ((Number) _objEnd).intValue();
 122  9
                 _objEnd = null;
 123  
             }
 124  1
             else if (_objEnd instanceof String)
 125  
             {
 126  0
                 _end = Integer.parseInt((String) _objEnd);
 127  0
                 _objEnd = null;
 128  
             }
 129  
         }
 130  10
         if (_objStep != null)
 131  
         {
 132  8
             if (_objStep instanceof Number)
 133  
             {
 134  7
                 _step = ((Number) _objStep).intValue();
 135  7
                 _objStep = null;
 136  
             }
 137  1
             else if (_objStep instanceof String)
 138  
             {
 139  0
                 _step = Integer.parseInt((String) _objStep);
 140  0
                 _objStep = null;
 141  
             }
 142  
         }
 143  
 
 144  10
         return this;
 145  
     }
 146  
 
 147  
     public void write (FastWriter out, Context context) 
 148  
         throws PropertyException, IOException
 149  
     {
 150  10
         int start = _start, end = _end, step = _step;
 151  10
         boolean error = false;
 152  
         
 153  
         // if necessary, do run-time evaluation of our
 154  
         // start, end, and step objects.
 155  
         // If object is null then it has been fully resolved during build.
 156  
         try {
 157  10
            if (_objStart != null)
 158  1
               start = evalAsInt(context, _objStart);
 159  9
            if (_objEnd != null)
 160  1
               end = evalAsInt(context, _objEnd);
 161  8
            if (_objStep != null)
 162  1
               step = evalAsInt(context, _objStep);
 163  
         }
 164  3
         catch (Exception e)
 165  
         {
 166  3
            out.write(context.getEvaluationExceptionHandler()
 167  
                    .expand(_iterator, context, e));
 168  3
            error = true;
 169  7
         }
 170  
 
 171  10
         if (!error)
 172  
         {
 173  
            // Check if no step explicitly assigned, if so auto-detect it
 174  7
            if (step == Integer.MAX_VALUE)
 175  
            {
 176  2
                step = (start > end) ? -1 : +1;
 177  
            }
 178  
    
 179  7
            if (step > 0)
 180  
            {
 181  33
                for ( ; start <= end; start += step)
 182  
                {
 183  15
                    _iterator.setValue(context, new Integer(start));
 184  15
                    _body.write(out, context);
 185  
                }
 186  
            }
 187  4
            else if (step < 0)
 188  
            {
 189  43
                for ( ; start >= end; start += step)
 190  
                {
 191  20
                    _iterator.setValue(context, new Integer(start));
 192  20
                    _body.write(out, context);
 193  
                }
 194  
            }
 195  
            else
 196  
            {
 197  1
               PropertyException pe = new PropertyException.UndefinedVariableException();
 198  1
               pe.setMessage("#count: step cannot be 0.");
 199  1
               out.write(context.getEvaluationExceptionHandler()
 200  
                       .expand(_iterator, context, pe));
 201  
            }
 202  
         }
 203  10
     }
 204  
     
 205  
     private int evalAsInt(Context context, Object o) throws PropertyException
 206  
     {
 207  3
        if (o != null)
 208  
        {
 209  6
           while (o instanceof Macro && !(o == UndefinedMacro.getInstance()))
 210  
           {
 211  3
              o = ((Macro)o).evaluate(context);             
 212  
           }
 213  3
           if (o == UndefinedMacro.getInstance()){
 214  3
              PropertyException pe = new PropertyException.UndefinedVariableException();
 215  3
              pe.setMessage("Undefined expression in #count directive.");
 216  3
              throw pe;                
 217  
           }
 218  
        }
 219  0
        if (o == null)
 220  
        {
 221  0
           PropertyException pe = new PropertyException.UndefinedVariableException();
 222  0
           pe.setMessage("Null expression in #count directive.");
 223  0
           throw pe;
 224  
        }
 225  0
        if (o instanceof Number)
 226  0
            return ((Number) o).intValue();
 227  
        try {
 228  0
           return Integer.parseInt(o.toString());
 229  
        }
 230  0
        catch (NumberFormatException e)
 231  
        {
 232  0
           PropertyException pe = new PropertyException.UndefinedVariableException();
 233  0
           pe.setMessage("Invalid numeric expression in #count directive: [" + o + "]");
 234  0
           throw pe;                
 235  
        }
 236  
     }
 237  
 }