Author: nbubna
Date: Sat Feb 21 05:56:51 2009
New Revision: 746439

URL: http://svn.apache.org/viewvc?rev=746439&view=rev
Log:
VELOCITY-704 continuation of r746438 (forgot to do svn add first)

Added:
    
velocity/engine/trunk/src/java/org/apache/velocity/runtime/directive/ForeachScope.java
   (with props)
    
velocity/engine/trunk/src/java/org/apache/velocity/runtime/directive/Scope.java 
  (with props)
    
velocity/engine/trunk/src/java/org/apache/velocity/runtime/directive/StopCommand.java
   (with props)
    velocity/engine/trunk/src/test/org/apache/velocity/test/ScopeTestCase.java  
 (with props)

Added: 
velocity/engine/trunk/src/java/org/apache/velocity/runtime/directive/ForeachScope.java
URL: 
http://svn.apache.org/viewvc/velocity/engine/trunk/src/java/org/apache/velocity/runtime/directive/ForeachScope.java?rev=746439&view=auto
==============================================================================
--- 
velocity/engine/trunk/src/java/org/apache/velocity/runtime/directive/ForeachScope.java
 (added)
+++ 
velocity/engine/trunk/src/java/org/apache/velocity/runtime/directive/ForeachScope.java
 Sat Feb 21 05:56:51 2009
@@ -0,0 +1,69 @@
+package org.apache.velocity.runtime.directive;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.    
+ */
+
+/**
+ * This represents scoping and metadata for #foreach,
+ * adding index, count, hasNext, isFirst and isLast info.
+ *
+ * @author Nathan Bubna
+ * @version $Id$
+ */
+public class ForeachScope extends Scope
+{
+    protected int index = -1;
+    protected boolean hasNext = false;
+
+    public ForeachScope(Object owner, Object replaces)
+    {
+        super(owner, replaces);
+    }
+
+    public int getIndex()
+    {
+        return index;
+    }
+
+    public int getCount()
+    {
+        return index + 1;
+    }
+
+    public boolean hasNext()
+    {
+        return getHasNext();
+    }
+
+    public boolean getHasNext()
+    {
+        return hasNext;
+    }
+
+    public boolean isFirst()
+    {
+        return index < 1;
+    }
+
+    public boolean isLast()
+    {
+        return !hasNext;
+    }
+
+}

Propchange: 
velocity/engine/trunk/src/java/org/apache/velocity/runtime/directive/ForeachScope.java
------------------------------------------------------------------------------
    svn:executable = *

Added: 
velocity/engine/trunk/src/java/org/apache/velocity/runtime/directive/Scope.java
URL: 
http://svn.apache.org/viewvc/velocity/engine/trunk/src/java/org/apache/velocity/runtime/directive/Scope.java?rev=746439&view=auto
==============================================================================
--- 
velocity/engine/trunk/src/java/org/apache/velocity/runtime/directive/Scope.java 
(added)
+++ 
velocity/engine/trunk/src/java/org/apache/velocity/runtime/directive/Scope.java 
Sat Feb 21 05:56:51 2009
@@ -0,0 +1,124 @@
+package org.apache.velocity.runtime.directive;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.    
+ */
+
+import java.util.AbstractMap;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * This handles context scoping and metadata for directives.
+ *
+ * @author Nathan Bubna
+ * @version $Id$
+ */
+public class Scope extends AbstractMap
+{
+    private final Map storage = new HashMap();
+    protected final Object replaced;
+    protected final Scope parent;
+    protected final Object owner;
+
+    public Scope(Object owner, Object previous)
+    {
+        this.owner = owner;
+        if (previous instanceof Scope)
+        {
+            this.parent = (Scope)previous;
+            // keep easy access to the user's object
+            this.replaced = this.parent.replaced;
+        }
+        else
+        {
+            this.parent = null;
+            this.replaced = previous;
+        }
+    }
+
+    public Set entrySet()
+    {
+        return storage.entrySet();
+    }
+
+    public Object put(Object key, Object value)
+    {
+        return storage.put(key, value);
+    }
+
+    /**
+     * TODO: remove or protect this method from template
+     * usage once the #stop directive is retrofitted to
+     * take Scope objects as an optional parameter.
+     */
+    public void stop()
+    {
+        throw new StopCommand(owner);
+    }
+
+    /**
+     * Returns the number of control arguments of this type
+     * that are stacked up.  This is the distance between this
+     * instance and the topmost instance, plus one. This value
+     * will never be negative or zero.
+     */
+    public int getDepth()
+    {
+        if (parent == null)
+        {
+            return 1;
+        }
+        return parent.getDepth() + 1;
+    }
+
+    /**
+     * Returns the topmost parent control reference, retrieved
+     * by simple recursion on {...@link #getParent}.
+     */
+    public Scope getTopmost()
+    {
+        if (parent == null)
+        {
+            return this;
+        }
+        return parent.getTopmost();
+    }
+
+    /**
+     * Returns the parent control reference overridden by the placement
+     * of this instance in the context.
+     */
+    public Scope getParent()
+    {
+        return parent;
+    }
+
+    /**
+     * Returns the user's context reference overridden by the placement
+     * of this instance in the context.  If there was none (as is hoped),
+     * then this will return null.  This never returns parent controls;
+     * those are returned by {...@link #getParent}.
+     */
+    public Object getReplaced()
+    {
+        return replaced;
+    }
+
+}

Propchange: 
velocity/engine/trunk/src/java/org/apache/velocity/runtime/directive/Scope.java
------------------------------------------------------------------------------
    svn:executable = *

Added: 
velocity/engine/trunk/src/java/org/apache/velocity/runtime/directive/StopCommand.java
URL: 
http://svn.apache.org/viewvc/velocity/engine/trunk/src/java/org/apache/velocity/runtime/directive/StopCommand.java?rev=746439&view=auto
==============================================================================
--- 
velocity/engine/trunk/src/java/org/apache/velocity/runtime/directive/StopCommand.java
 (added)
+++ 
velocity/engine/trunk/src/java/org/apache/velocity/runtime/directive/StopCommand.java
 Sat Feb 21 05:56:51 2009
@@ -0,0 +1,52 @@
+package org.apache.velocity.runtime.directive;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.    
+ */
+
+/**
+ * Stop command for directive Control objects.  In an ideal JDK,
+ * this would be able to extend a RuntimeThrowable class, but we
+ * don't have that. So to avoid the interface changes needed by
+ * extending Throwable and the potential errant catches were we
+ * to extend RuntimeException, we'll have to extend Error,
+ * despite the fact that this is never an error.
+ *
+ * @author Nathan Bubna
+ * @version $Id$
+ */
+public class StopCommand extends Error
+{
+    private Object stopMe;
+
+    public StopCommand(Object stopMe)
+    {
+        this.stopMe = stopMe;
+    }
+
+    public String getMessage()
+    {
+        // only create a useful message if requested (which is unlikely)
+        return "StopCommand for "+stopMe;
+    }
+
+    public boolean isFor(Object that)
+    {
+        return (that == stopMe);
+    }
+}

Propchange: 
velocity/engine/trunk/src/java/org/apache/velocity/runtime/directive/StopCommand.java
------------------------------------------------------------------------------
    svn:executable = *

Added: 
velocity/engine/trunk/src/test/org/apache/velocity/test/ScopeTestCase.java
URL: 
http://svn.apache.org/viewvc/velocity/engine/trunk/src/test/org/apache/velocity/test/ScopeTestCase.java?rev=746439&view=auto
==============================================================================
--- velocity/engine/trunk/src/test/org/apache/velocity/test/ScopeTestCase.java 
(added)
+++ velocity/engine/trunk/src/test/org/apache/velocity/test/ScopeTestCase.java 
Sat Feb 21 05:56:51 2009
@@ -0,0 +1,250 @@
+package org.apache.velocity.test;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.    
+ */
+
+import java.util.HashMap;
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.runtime.RuntimeConstants;
+import org.apache.velocity.runtime.directive.Scope;
+
+/**
+ * This class tests the directive scope controls
+ */
+public class ScopeTestCase extends BaseTestCase
+{
+    public ScopeTestCase(String name)
+    {
+       super(name);
+    }
+
+    public void testRootTemplateMergeScope()
+    {
+        addTemplate("foo", "foo$template.stop()bar");
+        assertTmplEquals("foo", "foo");
+        assertNull(context.get("template"));
+    }
+
+    public void testParseScope()
+    {
+        addTemplate("test", "$template.depth"+
+                            "$!parse.parent.depth"+
+                            "#set( $template.foo = 'bar' )"+
+                            "$template.foo"+
+                            "$template.stop()"+
+                            "woogie");
+        assertEvalEquals("1bar", "#parse( 'test' )");
+        assertNull(context.get("template"));
+    }
+
+    public void testNestedParseScope()
+    {
+        HashMap grab = new HashMap();
+        context.put("grab", grab);
+
+        addTemplate("inner", "Inner depth: $template.depth"+
+                             "#set( $template.foo = '?' )"+
+                             "$!grab.put('inner',$template)"+
+                             "$template.stop()$template.foo");
+        addTemplate("outer", "#set( $template.foo = '!' )"+
+                             "Outer depth: $template.depth "+
+                             "#parse('inner')"+
+                             "$!grab.put('outer', $template)"+
+                             "$template.foo");
+        assertEvalEquals("Outer depth: 1 Inner depth: 2!", "#parse('outer')");
+        // make extra sure that the outer control was restored after the stop
+        assertFalse(grab.get("inner") == grab.get("outer"));
+        // make sure the outer control was cleaned up
+        assertNull(context.get("template"));
+
+        addTemplate("3", "$template.topmost.foo#set( $template.topmost.foo = 
'bar' )");
+        addTemplate("2", "#parse( '3' )$!parse.foo");
+        addTemplate("1", "#set( $template.foo = 'foo' 
)#parse('2')$template.foo");
+        assertEvalEquals("foobar", "#parse('1')$!parse");
+        // make sure the top control was cleaned up
+        assertNull(context.get("template"));
+    }
+
+    public void testForeachScope()
+    {
+        String template = "#foreach( $i in [0..2] )"+
+                          "#if( $i > 1 )$foreach.stop()#end"+
+                          "$foreach.index:$foreach.count:$foreach.hasNext,"+
+                          "#end";
+        assertEvalEquals("0:1:true,1:2:true,", template);
+        assertNull(context.get("foreach"));
+    }
+
+    public void testNestedForeachScope()
+    {
+        String template = "#foreach( $i in [1..5] )"+
+                            "#foreach( $j in [1..2] )"+
+                              "#if ( $i > $foreach.count + $foreach.index + 
$foreach.depth )$foreach.topmost.stop()#end"+
+                            "#end"+
+                            "$i"+
+                          "#end";
+        assertEvalEquals("123", template);
+        assertNull(context.get("foreach"));
+    }
+
+    public void testMacroScope()
+    {
+        String template = "#macro( foo $i )"+
+                          "#if($i > 2 )$macro.stop()#end"+
+                          "$i#end"+
+                          "#foo( 0 )#foo( 1 )#foo( 2 )";
+        assertEvalEquals("012", template);
+        assertNull(context.get("macro"));
+    }
+
+    public void testRecursiveMacroScope()
+    {
+        String template = "#macro( foo )$macro.depth"+
+                          "#if($macro.depth > 2 )$macro.topmost.stop()#end"+
+                          "#foo()#end#foo()";
+        assertEvalEquals("123", template);
+        assertNull(context.get("macro"));
+    }
+
+    public void testNestedMacroScope()
+    {
+        String template = "#macro( a )$macro.depth#set($macro.c = 
'a')$macro.c#end"+
+                          "#macro( b )#set($macro.c = 'b' )#a()$macro.c#end"+
+                          "#b()";
+        assertEvalEquals("2ab", template);
+        assertNull(context.get("macro"));
+    }
+
+    public void testBodyMacroScope()
+    {
+        String template = "#macro( foo $bar )$bodyContent$macro.bar#end"+
+                          "#...@foo( 'bar' )#set( $macro.bar = 'foo'+$bar )"+
+                          "#set( $foo.d = $foo.depth )$foo.d #end";
+        assertEvalEquals("1 foobar", template);
+        assertNull(context.get("foo"));
+        assertNull(context.get("macro"));
+    }
+
+    public void testRecursiveBodyMacroScope()
+    {
+        engine.setProperty(RuntimeConstants.VM_MAX_DEPTH, "5");
+        String template = "#macro( foo )$bodyContent$macro.i#end"+
+                          "#...@foo()#set( $macro.i = \"$!macro.i$foo.depth,\" 
)"+
+                          "$!bodyContent#end";
+        assertEvalEquals("1,2,3,4,5,", template);
+        assertNull(context.get("foo"));
+        assertNull(context.get("macro"));
+    }
+
+    public void testDefineScope()
+    {
+        String template = "#define( $foo )#set( $define.bar = 
'bar'+$define.depth )$define.bar#end$foo";
+        assertEvalEquals("bar1", template);
+        assertNull(context.get("define"));
+    }
+
+    public void testNestedDefineScope()
+    {
+        String template = "#define($a)$b c#end"+
+                          "#define($b)$define.depth$define.topmost.stop()#end"+
+                          "$a";
+        assertEvalEquals("2", template);
+        assertNull(context.get("define"));
+    }
+
+    public void testRecursiveDefineScope()
+    {
+        engine.setProperty(RuntimeConstants.DEFINE_DIRECTIVE_MAXDEPTH, "10");
+        String template = "#define($a)$define.depth"+
+                          "#if($define.depth == 5)$define.stop()#end,$a#end$a";
+        assertEvalEquals("1,2,3,4,5", template);
+        assertNull(context.get("define"));
+    }
+
+    public void testRootEvaluateScope()
+    {
+        assertEvalEquals("1", "$evaluate.depth");
+        assertEvalEquals("foo", "foo$evaluate.stop()bar");
+        assertNull(context.get("evaluate"));
+    }
+
+    public void testEvaluateScope()
+    {
+        context.put("h", "#");
+        context.put("d", "$");
+        String template = "${h}set( ${d}evaluate.foo = 'bar' )"+
+                          "${d}evaluate.foo ${d}evaluate.depth";
+        addTemplate("eval", "#evaluate(\""+template+"\")");
+        assertTmplEquals("bar 1", "eval");
+        assertNull(context.get("evaluate"));
+        assertNull(context.get("template"));
+    }
+
+    public void testNestedEvaluateScope()
+    {
+        context.put("h", "#");
+        context.put("d", "$");
+        addTemplate("e", "#evaluate(\"${h}evaluate( 
'${d}evaluate.depth${d}evaluate.stop() blah' )\")");
+        assertTmplEquals("2", "e");
+        assertNull(context.get("evaluate"));
+        assertNull(context.get("template"));
+    }
+
+    public void testTurningOffTemplateScope()
+    {
+        engine.setProperty("template."+RuntimeConstants.PROVIDE_SCOPE_CONTROL, 
"false");
+        // root
+        addTemplate("test", "$template.depth");
+        assertTmplEquals("$template.depth", "test");
+        // #parse
+        assertEvalEquals("$template.depth", "#parse('test')");
+    }
+
+    public void testTurningOffEvaluateScope()
+    {
+        engine.setProperty("evaluate."+RuntimeConstants.PROVIDE_SCOPE_CONTROL, 
"false");
+        // root
+        assertSchmoo("$evaluate.depth");
+        // #evaluate
+        assertEvalEquals("$evaluate.depth", "#evaluate( '$evaluate.depth' )");
+    }
+
+    public void testTurningOffMacroScope()
+    {
+        engine.setProperty("macro."+RuntimeConstants.PROVIDE_SCOPE_CONTROL, 
"false");
+        engine.setProperty("foo."+RuntimeConstants.PROVIDE_SCOPE_CONTROL, 
"false");
+        // macro definition
+        assertEvalEquals("$macro", "#macro(a)$macro#end#a()");
+        // macro body
+        assertEvalEquals("$macro $foo", 
"#macro(foo)$bodycontent#e...@foo()$macro $foo#end");
+    }
+
+    public void testTurningOffDefineScope()
+    {
+        engine.setProperty("define."+RuntimeConstants.PROVIDE_SCOPE_CONTROL, 
"false");
+        assertEvalEquals("$define", "#define($a)$define#end$a");
+    }
+
+    public void testTurningOffForeachScope()
+    {
+        engine.setProperty("foreach."+RuntimeConstants.PROVIDE_SCOPE_CONTROL, 
"false");
+        assertEvalEquals("$foreach$foreach", "#foreach($i in 
[0..1])$foreach#end");
+    }
+
+}

Propchange: 
velocity/engine/trunk/src/test/org/apache/velocity/test/ScopeTestCase.java
------------------------------------------------------------------------------
    svn:executable = *


Reply via email to