Support for node-local context data introspection caching : don't do introspection every visit now, but use data from the InternalContext cache if things are hunky.
PR:
Obtained from:
Submitted by:
Reviewed by:


git-svn-id: https://svn.apache.org/repos/asf/jakarta/velocity/trunk@73937 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/src/java/org/apache/velocity/runtime/directive/Foreach.java b/src/java/org/apache/velocity/runtime/directive/Foreach.java
index 7270ba0..6b57960 100644
--- a/src/java/org/apache/velocity/runtime/directive/Foreach.java
+++ b/src/java/org/apache/velocity/runtime/directive/Foreach.java
@@ -78,6 +78,7 @@
 import org.apache.velocity.runtime.exception.ReferenceException;
 
 import org.apache.velocity.util.introspection.Introspector;
+import org.apache.velocity.util.introspection.IntrospectionCacheData;
 
 /**
  * Foreach directive used for moving through arrays,
@@ -85,7 +86,7 @@
  *
  * @author <a href="mailto:jvanzyl@periapt.com">Jason van Zyl</a>
  * @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a>
- * @version $Id: Foreach.java,v 1.27 2000/12/04 02:06:52 geirm Exp $
+ * @version $Id: Foreach.java,v 1.28 2000/12/12 23:45:38 geirm Exp $
  */
 public class Foreach extends Directive
 {
@@ -105,6 +106,8 @@
         return BLOCK;
     }        
 
+    private final static int UNKNOWN = -1;
+    
     /**
      * Flag to indicate that the list object being used
      * in an array.
@@ -122,16 +125,7 @@
      * is a Map.
      */
     private final static int INFO_MAP = 3;
-    
-    /**
-     * Flag to indicate that the list object is
-     * empty. This is perfectly valid, and nothing
-     * will be rendered. When the list object
-     * contains values, then the flag will be
-     * altered and the rendering will occur.
-     */
-    private final static int INFO_EMPTY_LIST_OBJECT = 4;
-    
+  
     /**
      * The name of the variable to use when placing
      * the counter value into the context. Right
@@ -174,73 +168,126 @@
         elementKey = node.jjtGetChild(0).getFirstToken().image.substring(1);
     }
 
+    /**
+     *  returns an Iterator to the collection in the #foreach()
+     *
+     *  @param context  current context
+     *  @param node   AST node
+     *  @return Iterator to do the dataset
+     */
     private Iterator getIterator( Context context, Node node )
     {
-        Object listObject = null;
-  
         /*
          *  get our list object, and punt if it's null.
          */
 
-        listObject = node.jjtGetChild(2).value(context);
+        Object listObject = node.jjtGetChild(2).value(context);
         
         if (listObject == null)
             return null;
 
+        /*
+         *  See if we already know what type this is. Use the introspection cache
+         */
+
+        int type = UNKNOWN;
+
+        IntrospectionCacheData icd = context.icacheGet( this ); 
+        Class c = listObject.getClass();
+
+        /*
+         *  if we have an entry in the cache, and the Class we have
+         *  cached is the same as the Class of the data object
+         *  then we are ok
+         */
+
+        if ( icd != null && icd.contextData == c )
+        {
+            /* dig the type out of the cata object */
+            type = ((Integer) icd.thingy ).intValue();
+        }
+
         /* 
-         * Figure out what type of object the list
+         * If we still don't know what this is, 
+         * figure out what type of object the list
          * element is, and get the iterator for it
          */
 
-        if (Introspector.implementsMethod(listObject, "iterator"))
+        if ( type == UNKNOWN )
         {
-            if (((Collection) listObject).size() == 0)
-                return null;
+            if (listObject instanceof Object[])
+                type = INFO_ARRAY;
+            else if (Introspector.implementsMethod(listObject, "iterator"))
+                type = INFO_ITERATOR;
+            else if (Introspector.implementsMethod(listObject, "values"))
+                type = INFO_MAP;
 
-            return ((Collection) listObject).iterator();        
+            /*
+             *  if we did figure it out, cache it
+             */
+
+            if ( type != UNKNOWN )
+            {
+                icd = new IntrospectionCacheData();
+                icd.thingy = new Integer( type );
+                icd.contextData = c;
+                context.icachePut( this, icd );
+            }
         }
-        else if (listObject instanceof Object[])
-        {
+
+        /*
+         *  now based on the type from either cache or examination...
+         */
+
+        switch( type ) {
+            
+        case INFO_ITERATOR :
+            
+            if (((Collection) listObject).size() == 0)
+                return null;    
+           
+            return ((Collection) listObject).iterator();        
+            
+        case INFO_ARRAY:
+            
             Object[] arrayObject = ((Object[]) listObject);
             
             if (arrayObject.length == 0)
-                return null;
+                return null; 
             
             return new ArrayIterator( arrayObject );
-        }            
-        else if (Introspector.implementsMethod(listObject, "values"))
-        {
+           
+        case INFO_MAP:          
+
             if (((Map) listObject).size() == 0)
                 return null;
- 
+
             return ((Map) listObject).values().iterator();
-        }
-        else
-        {
-            /*
-             *  we have no clue what this is
-             */
+        
+        default:
+        
+            /*  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;
-        }            
+        }
     }
 
+    /**
+     *  renders the #foreach() block
+     */
     public boolean render(Context context, Writer writer, Node node)
         throws IOException
-    {
-        int counter;
-        Iterator i;
-        
+    {        
         /*
          *  do our introspection to see what our collection is
          */
 
-        i = getIterator( context, node );
+        Iterator i = getIterator( context, node );
    
         if ( i == null)
             return false;
         
-        counter = COUNTER_INITIAL_VALUE;
+        int counter = COUNTER_INITIAL_VALUE;
         
         /*
          *  save the element key if there is one
@@ -270,3 +317,6 @@
     }
 
 }
+
+
+