geirm       00/12/03 18:06:53

  Modified:    src/java/org/apache/velocity/runtime/directive Foreach.java
  Log:
  Big change is that introspection etc is handled at exec time ( via render() ) rather 
than setup through init.  Small change is that we save and restore the value of the 
loop reference if it exists...
  
  Revision  Changes    Path
  1.27      +62 -134   
jakarta-velocity/src/java/org/apache/velocity/runtime/directive/Foreach.java
  
  Index: Foreach.java
  ===================================================================
  RCS file: 
/home/cvs/jakarta-velocity/src/java/org/apache/velocity/runtime/directive/Foreach.java,v
  retrieving revision 1.26
  retrieving revision 1.27
  diff -u -r1.26 -r1.27
  --- Foreach.java      2000/11/28 04:16:21     1.26
  +++ Foreach.java      2000/12/04 02:06:52     1.27
  @@ -85,7 +85,7 @@
    *
    * @author <a href="mailto:[EMAIL PROTECTED]">Jason van Zyl</a>
    * @author <a href="mailto:[EMAIL PROTECTED]">Geir Magnusson Jr.</a>
  - * @version $Id: Foreach.java,v 1.26 2000/11/28 04:16:21 jvanzyl Exp $
  + * @version $Id: Foreach.java,v 1.27 2000/12/04 02:06:52 geirm Exp $
    */
   public class Foreach extends Directive
   {
  @@ -157,105 +157,71 @@
        * it is immutable.
        */
       private String elementKey;
  -    
  +
  +    /**
  +     *  simple init - init the tree and get the elementKey from
  +     *  the AST
  +     */
       public void init(Context context, Node node) throws Exception
       {
  -        Object sampleElement = null;
  -        Object listObject = null;
  -        
  +        super.init( context, node );
  +
  +        /*
  +         *  this is really the only thing we can do here as everything
  +         *  else is context sensitive
  +         */
  +
           elementKey = node.jjtGetChild(0).getFirstToken().image.substring(1);
  +    }
   
  +    private Iterator getIterator( Context context, Node node )
  +    {
  +        Object listObject = null;
  +  
           /*
  -         * This is a refence node and it needs to
  -         * be inititialized.
  +         *  get our list object, and punt if it's null.
            */
  -        node.jjtGetChild(2).init(context, null);
  +
           listObject = node.jjtGetChild(2).value(context);
           
  -        /* 
  -         * If the listObject is null then we know that this
  -         * whole foreach directive is useless. We need to
  -         * throw a ReferenceException which is caught by
  -         * the SimpleNode.init() and logged. But we also need
  -         * to set a flag so that the rendering of this node
  -         * is ignored as the output would be useless.
  -         */
  -        
  -        /* 
  -         * Slight problem with this approach. The list object
  -         * may have been #set previously, this usually wouldn't
  -         * be the case, but this check should be moved into
  -         * the rendering phase.
  -         */
           if (listObject == null)
  -        {
  -            node.setInvalid();
  -            throw new ReferenceException("#foreach", node.jjtGetChild(2));
  -        }                
  +            return null;
   
           /* 
            * Figure out what type of object the list
  -         * element is so that we don't have to do it
  -         * everytime the node is traversed.
  -         * if (listObject instanceof Object[])
  +         * element is, and get the iterator for it
            */
  -        if (listObject instanceof Object[])
  +
  +        if (Introspector.implementsMethod(listObject, "iterator"))
           {
  +            if (((Collection) listObject).size() == 0)
  +                return null;
  +
  +            return ((Collection) listObject).iterator();        
  +        }
  +        else if (listObject instanceof Object[])
  +        {
               Object[] arrayObject = ((Object[]) listObject);
               
               if (arrayObject.length == 0)
  -            {
  -                node.setInfo(INFO_EMPTY_LIST_OBJECT);
  -            }                
  -            else
  -            {
  -                node.setInfo(INFO_ARRAY);
  -                sampleElement = arrayObject[0];
  -            }                    
  +                return null;
  +            
  +            return new ArrayIterator( arrayObject );
           }            
  -        else if (Introspector.implementsMethod(listObject, "iterator"))
  -        {
  -            if (((Collection) listObject).size() == 0)
  -            {
  -                node.setInfo(INFO_EMPTY_LIST_OBJECT);
  -            }                
  -            else
  -            {
  -                node.setInfo(INFO_ITERATOR);
  -                sampleElement = ((Collection) listObject).iterator().next();
  -            }                    
  -        }
           else if (Introspector.implementsMethod(listObject, "values"))
           {
               if (((Map) listObject).size() == 0)
  -            {
  -                node.setInfo(INFO_EMPTY_LIST_OBJECT);
  -            }
  -            else
  -            {
  -                node.setInfo(INFO_MAP);
  -                sampleElement = ((Map) listObject).values().iterator().next();
  -            }                    
  +                return null;
  + 
  +            return ((Map) listObject).values().iterator();
           }
           else
  -        {
  -            // If it's not an array or an object that provides
  -            // an iterator then the node is invalid and should
  -            // not be rendered.
  -            node.setInvalid();
  -            Runtime.warn ("Could not determine type of iterator for #foreach loop 
");
  -            throw new NodeException ("Could not determine type of iterator for 
#foreach loop " , node);
  -        }            
  -        
  -        // This is a little trick so that we can initialize
  -        // all the blocks in the foreach  properly given
  -        // that there are references that refer to the
  -        // elementKey name.
  -        if (sampleElement != null)
           {
  -            context.put(elementKey, sampleElement);
  -            super.init(context, node);
  -            context.remove(elementKey);
  +            /*
  +             *  we have no clue what this is
  +             */
  +            Runtime.warn ("Could not determine type of iterator for #foreach loop 
for " +  node.jjtGetChild(2).getFirstToken().image);
  +            return null;
           }            
       }
   
  @@ -264,69 +230,24 @@
       {
           int counter;
           Iterator i;
  -        Object listObject = null;
           
           /*
  -         * If the node has been set to invalid then it is because
  -         * the list object placed in the context is not an array,
  -         * does not provide an Iterator, or is not Map. In these
  -         * cases there is nothing we can do, and nothing will
  -         * be rendered.
  +         *  do our introspection to see what our collection is
            */
  -        if (node.isInvalid())
  -            return false;
  -            
  -        if (node.getInfo() == INFO_EMPTY_LIST_OBJECT)
  -        {
  -            /*
  -             * If the list object had no elements
  -             * then lets try to init again. If the list
  -             * object is still empty then an exception
  -             * will be thrown again. But we will keep
  -             * trying!
  -             */
  -            synchronized(this)
  -            {
  -                /* 
  -                 * Check again for other threads that
  -                 * got through above. Don't want to
  -                 * have to synchronize on every render,
  -                 * but we don't want to init() multiple
  -                 * times here either. A few threads might
  -                 * get into this block, but only one thread
  -                 * will try the init().
  -                 */
  -                if (node.getInfo() == INFO_EMPTY_LIST_OBJECT)
  -                {
  -                    try
  -                    {
  -                        init(context, node);
  -                        
  -                        /*
  -                         * Check again, if the list is still
  -                         * empty then return false.
  -                         */
  -                        if (node.getInfo() == INFO_EMPTY_LIST_OBJECT)
  -                            return false;
  -                    }
  -                    catch (Exception e)
  -                    {
  -                        return false;
  -                    }
  -                }
  -            }
  -        }
  -    
  -        listObject = node.jjtGetChild(2).value(context);
   
  -        if (node.getInfo() == INFO_ARRAY)
  -            i = new ArrayIterator((Object[]) listObject);
  -        else if (node.getInfo() == INFO_MAP)
  -            i = ((Map) listObject).values().iterator();
  -        else            
  -            i = ((Collection) listObject).iterator();
  +        i = getIterator( context, node );
  +   
  +        if ( i == null)
  +            return false;
           
           counter = COUNTER_INITIAL_VALUE;
  +        
  +        /*
  +         *  save the element key if there is one
  +         */
  +
  +        Object o = context.get( elementKey );
  +
           while (i.hasNext())
           {
               context.put(COUNTER_NAME, new Integer(counter));
  @@ -337,7 +258,14 @@
   
           context.remove(COUNTER_NAME);
           context.remove(elementKey);
  -    
  +
  +        /*
  +         *  restores element key if exists
  +         */
  +
  +        if (o != null)
  +            context.put( elementKey, o );
  +
           return true;
       }
   
  
  
  

Reply via email to