Index: DefaultInterpreter.java
===================================================================
--- DefaultInterpreter.java	(revision 44153)
+++ DefaultInterpreter.java	(revision 44154)
@@ -17,6 +17,7 @@
 import org.gvsig.expressionevaluator.Codes;
 import org.gvsig.expressionevaluator.ExpressionRuntimeException;
 import org.gvsig.expressionevaluator.Function;
+import org.gvsig.expressionevaluator.impl.DefaultCodeBuilder.RecursionControlSupport;
 import org.gvsig.expressionevaluator.impl.function.operator.BinaryOperator;
 import org.gvsig.expressionevaluator.impl.function.programming.CallMethodFunction;
 import org.gvsig.expressionevaluator.impl.function.operator.UnaryOperator;
@@ -24,6 +25,8 @@
 
 public class DefaultInterpreter implements Interpreter {
 
+    public static int DEFAULT_MAX_RECURSION_LIMIT = 20;
+    
     private class DefaultCache implements Cache {
         
         private Map<Pair<Object,Object>,Pair<Object,Long>> data;
@@ -83,9 +86,11 @@
     private Double accuracy;
     private Code currentCode;
     private Cache cache;
+    private int maxRecursionLimit;
 
     public DefaultInterpreter() {
         this.cache = new DefaultCache();
+        this.maxRecursionLimit = DEFAULT_MAX_RECURSION_LIMIT;
     }
 
     @Override
@@ -95,10 +100,17 @@
         if( this.symbolTable!=null ) {
             other.symbolTable = this.symbolTable.clone();
         }
-        
         return other;
     }
+    
+    public int getMaxRecursionLimit() {
+        return this.maxRecursionLimit;
+    }
             
+    public void setMaxRecursionLimit(int limit) {
+        this.maxRecursionLimit = limit;
+    }
+    
     @Override
     public Cache getCache() {
         return this.cache;
@@ -159,96 +171,110 @@
     }
 
     public Object runCode(Code code) throws Exception {
+        RecursionControlSupport recursionControl = null;
+        if( code instanceof RecursionControlSupport ) {
+            recursionControl = (RecursionControlSupport) code;
+            if( !recursionControl.enterCode(this.maxRecursionLimit) ) {
+                recursionControl.resetRecursionState();
+                throw new ExpressionRuntimeException(code, I18N.Maximum_recursion_limit_exceeded());
+            }
+        }
+        Object value = null;
         this.currentCode = code;
-        Object value = null;
-        switch( code.code() ) {
-        case Code.CONSTANT:
-            value = ((Constant) code).value();
-            break;
+        try {
+            switch( code.code() ) {
+            case Code.CONSTANT:
+                value = ((Constant) code).value();
+                break;
 
-        case Code.IDENTIFIER:
-            String name = ((Identifier) code).name();
-            if( !this.getSymbolTable().exists(name) ) {
-                throw new ExpressionRuntimeException(
-                        code, 
-                        I18N.Undefined_variable_XIdentifierX(name),
-                        I18N.Use_single_quotes_to_enter_literal_strings()
-                );
-            }
-            value = this.getSymbolTable().value(name);
-            break;
+            case Code.IDENTIFIER:
+                String name = ((Identifier) code).name();
+                if( !this.getSymbolTable().exists(name) ) {
+                    throw new ExpressionRuntimeException(
+                            code, 
+                            I18N.Undefined_variable_XIdentifierX(name),
+                            I18N.Use_single_quotes_to_enter_literal_strings()
+                    );
+                }
+                value = this.getSymbolTable().value(name);
+                break;
 
-        case Code.METHOD: {
-                Method method = (Method) code;
-                Function function = method.function();
-                if( function == null ) {
-                    Object obj = this.runCode(method.obj());
-                    if( obj == null ) {
-                        throw new NullPointerException("An object pointer was expected to invoke method "+method.methodname()+" and a null was received");
+            case Code.METHOD: {
+                    Method method = (Method) code;
+                    Function function = method.function();
+                    if( function == null ) {
+                        Object obj = this.runCode(method.obj());
+                        if( obj == null ) {
+                            throw new NullPointerException("An object pointer was expected to invoke method "+method.methodname()+" and a null was received");
+                        }
+                        method.function(new CallMethodFunction(obj, method.methodname()));
                     }
-                    method.function(new CallMethodFunction(obj, method.methodname()));
                 }
-            }
-        case Code.CALLER:
-            Caller caller = (Caller) code;
-            Function function = caller.function();
-            if( function == null ) {
-                function = this.getSymbolTable().function(caller.name());
+            case Code.CALLER:
+                Caller caller = (Caller) code;
+                Function function = caller.function();
                 if( function == null ) {
-                    throw new ExpressionRuntimeException(code, I18N.Undefined_function_XIdentifierX(caller.name()));
+                    function = this.getSymbolTable().function(caller.name());
+                    if( function == null ) {
+                        throw new ExpressionRuntimeException(code, I18N.Undefined_function_XIdentifierX(caller.name()));
+                    }
+                    caller.function(function);
                 }
-                caller.function(function);
-            }
-            Codes args = caller.args();
-            try {
-                switch( caller.type() ) {
-                case Caller.UNARY_OPERATOR:
-                    if( args == null || args.size() != 1 ) {
-                        throw new ExpressionRuntimeException(code, I18N.Number_of_argument_mistmatch_in_operator_XIdentifierX_expected_1_got_XargcX(function.name(),args==null?0:args.size()));
-                    }
-                    value = ((UnaryOperator) function).call(this, runCode(args.get(0)));
-                    break;
+                Codes args = caller.args();
+                try {
+                    switch( caller.type() ) {
+                    case Caller.UNARY_OPERATOR:
+                        if( args == null || args.size() != 1 ) {
+                            throw new ExpressionRuntimeException(code, I18N.Number_of_argument_mistmatch_in_operator_XIdentifierX_expected_1_got_XargcX(function.name(),args==null?0:args.size()));
+                        }
+                        value = ((UnaryOperator) function).call(this, runCode(args.get(0)));
+                        break;
 
-                case Caller.BINARY_OPERATOR:
-                    if( args == null || args.size() != 2 ) {
-                        throw new ExpressionRuntimeException(code, I18N.Number_of_argument_mistmatch_in_operator_XIdentifierX_expected_2_got_XargcX(function.name(),args==null?0:args.size()));
-                    }
-                    value = ((BinaryOperator) function).call(this, runCode(args.get(0)), runCode(args.get(1)));
-                    break;
+                    case Caller.BINARY_OPERATOR:
+                        if( args == null || args.size() != 2 ) {
+                            throw new ExpressionRuntimeException(code, I18N.Number_of_argument_mistmatch_in_operator_XIdentifierX_expected_2_got_XargcX(function.name(),args==null?0:args.size()));
+                        }
+                        value = ((BinaryOperator) function).call(this, runCode(args.get(0)), runCode(args.get(1)));
+                        break;
 
-                case Caller.FUNCTION:
-                    int argc = (args == null) ? 0 : args.size();
-                    if( !function.argc().contains(argc) ) {
-                        throw new ExpressionRuntimeException(code, I18N.Number_of_argument_mistmatch_in_function_XIdentifierX_expected_XexpectedX_got_XfoundX(function.name(),function.argc(),argc));
-                    }
-                    if( function.useArgumentsInsteadObjects() ) {
-                        value = function.call(this, args);
-                    } else {
-                        Object[] argvalues = new Object[argc];
-                        if( args != null ) {
-                            int i = 0;
-                            for( Code arg : args ) {
-                                argvalues[i++] = runCode(arg);
+                    case Caller.FUNCTION:
+                        int argc = (args == null) ? 0 : args.size();
+                        if( !function.argc().contains(argc) ) {
+                            throw new ExpressionRuntimeException(code, I18N.Number_of_argument_mistmatch_in_function_XIdentifierX_expected_XexpectedX_got_XfoundX(function.name(),function.argc(),argc));
+                        }
+                        if( function.useArgumentsInsteadObjects() ) {
+                            value = function.call(this, args);
+                        } else {
+                            Object[] argvalues = new Object[argc];
+                            if( args != null ) {
+                                int i = 0;
+                                for( Code arg : args ) {
+                                    argvalues[i++] = runCode(arg);
+                                }
                             }
+                            value = function.call(this, argvalues);
                         }
-                        value = function.call(this, argvalues);
                     }
+                } catch (RuntimeException ex) {
+                    throw ex;
+                } catch (Exception ex) {
+                    String argsstr = "???";
+                    try {
+                        argsstr = args.toString();
+                    } catch(Exception ex2) {
+                        // Ignore.
+                    }
+                    throw new ExpressionRuntimeException(code, I18N.Problems_calling_function_XIdentifierX_with_args_XargsX(function.name(),argsstr));
                 }
-            } catch (RuntimeException ex) {
-                throw ex;
-            } catch (Exception ex) {
-                String argsstr = "???";
-                try {
-                    argsstr = args.toString();
-                } catch(Exception ex2) {
-                    // Ignore.
-                }
-                throw new ExpressionRuntimeException(code, I18N.Problems_calling_function_XIdentifierX_with_args_XargsX(function.name(),argsstr));
+                break;
+
             }
-            break;
-            
+        } finally {
+            this.currentCode = null;
+            if( recursionControl!=null ) {
+                recursionControl.exitCode();
+            }
         }
-        this.currentCode = null;
         return value;
     }
 
