geirm       00/11/19 15:26:35

  Added:       src/java/org/apache/velocity/runtime/directive Macro.java
  Log:
  Pluggable Directive to handle the Velocimacro definition #macro( name arg1 arg2 
...).  Used inline in parsing to process and register the VM.  Does not render any 
output or init itself or its AST.
  
  Revision  Changes    Path
  1.1                  
jakarta-velocity/src/java/org/apache/velocity/runtime/directive/Macro.java
  
  Index: Macro.java
  ===================================================================
  /*
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 2000 The Apache Software Foundation.  All rights
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution, if
   *    any, must include the following acknowlegement:
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowlegement may appear in the software itself,
   *    if and wherever such third-party acknowlegements normally appear.
   *
   * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
   *    Foundation" must not be used to endorse or promote products derived
   *    from this software without prior written permission. For written
   *    permission, please contact [EMAIL PROTECTED]
   *
   * 5. Products derived from this software may not be called "Apache"
   *    nor may "Apache" appear in their names without prior written
   *    permission of the Apache Group.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   */
  
  
  /**
   *   Macro.java
   *
   *  Macro implements the macro definition directive of VTL.
   *
   *  example :
   *
   *  #macro( isnull $i )
   *     #if( $i )
   *         $i
   *      #end
   *  #end
   *
   *  This object is used at parse time to mainly process and register the 
   *  macro.  It is used inline in the parser when processing a directive.
   *
   * @author <a href="mailto:[EMAIL PROTECTED]">Geir Magnusson Jr.</a>
   * @version $Id: Macro.java,v 1.1 2000/11/19 23:26:35 geirm Exp $
   */
  
  package org.apache.velocity.runtime.directive;
  
  import java.io.Writer;
  import java.io.IOException;
  import java.util.TreeMap;
  
  import org.apache.velocity.Context;
  
  import org.apache.velocity.runtime.parser.node.Node;
  import org.apache.velocity.runtime.parser.Token;
  import org.apache.velocity.runtime.Runtime;
  
  public class Macro extends Directive
  {
      private static  boolean bDebug_ = false;
  
      public String getName() { return "macro"; }        
      public int getType() { return BLOCK; }
  
      /**
       *   render() doesn't do anything in the final output rendering.
       *   There is no output from a #macro() directive.
       */
      public boolean render(Context context, Writer writer, Node node)
          throws IOException 
      {
          /*
           *  do nothing : We never render.  The VelocimacroProxy object does that
           */
  
          return true;
      }
   
     public void init(Context context, Node node) throws Exception
      {
          /*
           *  again, don't do squat.  We want the AST of the macro block to hang off 
of this
           *  but we don't want to init it... it's useless...
           */
          return;
      }
  
      /**
       *  Used by Parser.java to process VMs withing the parsing process
       *
       *  processAndRegister() doesn't actually render the macro to the output
       *  Processes the macro body into the internal representation used by the
       *  VelocimacroProxy objects, and if not currently used, adds it
       *  to the macro Factory
       */ 
      public void processAndRegister( Node node)
          throws IOException
      {
          String strError = "";
  
          /*
           *  There must be at least one arg to  #macro,
           *  the name of the VM.  Note that 0 following 
           *  args is ok for naming blocks of HTML
           */
  
          int iNumArgs = node.jjtGetNumChildren();
  
          /*
           *  this number is the # of args + 1.  The + 1
           *  is for the block tree
           */
  
          if (iNumArgs < 2) 
          {
              
              /*
               *  error - they didn't name the macro or
               *  define a block
               */
              
              Runtime.error("#macro error : Velocimacro must have name as 1st argument 
to #macro()");
              return;
          }
  
          /*
           *  get the arguments to the use of the VM
           */
  
          String strArgs[] = getArgArray( node );
         
          /*
           *   now, try and eat the code block. Pass the root.
           */
          
          String strMacroArray[] = getASTAsStringArray( node.jjtGetChild(iNumArgs - 1) 
);
      
          /*
           *  make a big string out of our macro
           */
    
          StringBuffer strTemp  = new StringBuffer();
          
          for( int i=0; i < strMacroArray.length; i++)
              strTemp.append( strMacroArray[i] );
  
          String strMacro = strTemp.toString();    
      
          /*
           *  now, using the macro body string and the arg list, index all the tokens 
in the arglist
           */
  
          TreeMap tmArgIndexMap = getArgIndexMap( strMacro, strArgs);
  
          /*
           *  now, try to add it.  The Factory controls permissions, so just give it a 
whack...
           */
  
          int iMacroArgs = strArgs.length - 1;
  
          boolean bRet = Runtime.addVelocimacro( strArgs[0], strMacro, strArgs, 
strMacroArray, tmArgIndexMap );
   
          if (bRet)
              Runtime.info("Velocimacro : added new VM " +  strArgs[0] + "," +  
iMacroArgs );
          else
             Runtime.warn("Velocimacro : VM addition rejected : " +  strArgs[0] + " 
Check VM permissions and defaults."  );
          
          return;
      }
  
      /**
       *   using the macro body and the arg list, creates a TreeMap of the indices of 
the args in the body
       *   Makes for fast and efficient patching at runtime
       */
      private TreeMap getArgIndexMap( String strMacro, String strArgArray[] )
      {
          TreeMap tm = new TreeMap();
   
          /*
           *  run through the buffer for each paramter, and remember where they go.  
We have to do this 
           *  all at once to avoid confusing later replacement attempts with the 
activity of earlier ones
           */
  
          for (int i=1; i<strArgArray.length; i++)
          {
              /*
               *  keep going until we don't get any matches
               */
  
              int iIndex = 0;
  
              while( ( iIndex = strMacro.indexOf( strArgArray[i], iIndex )) != -1 )
              {
                  tm.put(new Integer( iIndex ), new Integer( i ));
                  iIndex++;
              }                    
          }
  
          return tm;
      }
  
      /**
       *  creates an array containing the literal
       *  strings in the macro arguement
       */
      private String[] getArgArray( Node node )
      {
          /*
           *  remember : this includes the block tree
           */
          
          int iNumArgs = node.jjtGetNumChildren();
        
          iNumArgs--;  // avoid the block tree...
        
          String strArgs[] = new String[ iNumArgs ];
        
          int i = 0;
        
          /*
           *  eat the args
           */
        
          while( i <  iNumArgs ) 
          {
              strArgs[i] = node.jjtGetChild(i).getFirstToken().image;
              i++;
          }
        
          if (bDebug_) 
          {
              System.out.println("Macro.getArgArray() : #args = " + iNumArgs );
              System.out.print( strArgs[0] + "(" );
            
              for (  i = 1; i < iNumArgs; i++) 
                  System.out.print(" " + strArgs[i] );
            
              System.out.println(" )");
          }
        
          return strArgs;
      }
  
     /**
       *  Returns an array of the literal rep of the AST
       */
      private String [] getASTAsStringArray( Node rootNode )
      {
          /*
           *  this assumes that we are passed in the root 
           *  node of the code block
           */
        
          Token t = rootNode.getFirstToken();
          Token tLast = rootNode.getLastToken();
  
          /*
           *  now, run down the part of the tree bounded by
           *  our first and last tokens
           */
  
          int iCount = 0;
          while( t != tLast ) 
          {
              iCount++;
              t = t.next;
          }
  
          /*
           *  account for the last one
           */
  
          iCount++;
  
          /*
           *  now, do it for real
           */
  
          String strArray[] = new String[iCount];
  
          iCount = 0;
          t = rootNode.getFirstToken();
  
          while( t != tLast ) 
          {
              strArray[iCount++] = t.image;
              t = t.next;
          }
  
          /*
           *  make sure we get the last one...
           */
  
          strArray[iCount] = t.image;
  
          return strArray;
      }
  }
  
  
  
  
  
  
  
  
  
  
  
  

Reply via email to