Index: trunk/org.gvsig.desktop/org.gvsig.desktop.compat.cdc/org.gvsig.fmap.dal/org.gvsig.fmap.dal.swing/org.gvsig.fmap.dal.swing.impl/src/main/java/org/gvsig/fmap/dal/swing/impl/featuretype/DefaultFeatureTypeAttributePanel.java
===================================================================
--- trunk/org.gvsig.desktop/org.gvsig.desktop.compat.cdc/org.gvsig.fmap.dal/org.gvsig.fmap.dal.swing/org.gvsig.fmap.dal.swing.impl/src/main/java/org/gvsig/fmap/dal/swing/impl/featuretype/DefaultFeatureTypeAttributePanel.java	(revision 44153)
+++ trunk/org.gvsig.desktop/org.gvsig.desktop.compat.cdc/org.gvsig.fmap.dal/org.gvsig.fmap.dal.swing/org.gvsig.fmap.dal.swing.impl/src/main/java/org/gvsig/fmap/dal/swing/impl/featuretype/DefaultFeatureTypeAttributePanel.java	(revision 44154)
@@ -12,6 +12,7 @@
 import javax.swing.SwingUtilities;
 import javax.swing.event.ChangeEvent;
 import javax.swing.event.ChangeListener;
+import org.apache.commons.lang3.ArrayUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.cresques.cts.IProjection;
 import org.gvsig.expressionevaluator.Expression;
@@ -304,6 +305,10 @@
                 default:
                     break;
             }
+            descriptor.setDataProfileName(
+                StringUtils.defaultIfBlank((String) this.cboDataProfile.getSelectedItem(),null)
+            );
+
             if( this.chkVirtualField.isSelected() && this.pickerVirtualField.isEnabled() ) {
                 Expression expression = this.pickerVirtualField.get();
                 if( expression==null ) {
@@ -314,12 +319,56 @@
                             descriptor.getFeatureType(),
                             expression
                     );
-                    descriptor.setFeatureAttributeEmulator(emulator);
+                    if( !emulator.isValid() ) {
+                        ThreadSafeDialogsManager dialogs = ToolsSwingLocator.getThreadSafeDialogsManager();
+                        dialogs.messageDialog(
+                                i18n.getTranslation("_The_expression_of_virtual_field_is_not_valid")+
+                                    "\n"+
+                                    emulator.getErrorMessage(), 
+                                i18n.getTranslation("_Warning"), 
+                                JOptionPane.WARNING_MESSAGE
+                        );
+                        return null;
+                    }
+                    for (String requiredFieldName : emulator.getRequiredFieldNames()) {
+                        if( StringUtils.equalsIgnoreCase(requiredFieldName, descriptor.getName()) ) {
+                            ThreadSafeDialogsManager dialogs = ToolsSwingLocator.getThreadSafeDialogsManager();
+                            dialogs.messageDialog(
+                                    i18n.getTranslation("_The_expression_of_virtual_field_is_not_valid")+
+                                        "\n\n"+
+                                        i18n.getTranslation("_The_expression_contains_a_reference_to_itself"), 
+                                    i18n.getTranslation("_Warning"), 
+                                    JOptionPane.WARNING_MESSAGE
+                            );
+                            return null;
+                        }
+                    }
+                    //
+                    // No se porque esto de abajo no funciona.
+                    // Nunca se lanza la excepcion en el get(Feature) de
+                    // DefaultFeatureAttributeEmulatorExpression.
+                    //
+//                    try {
+//                        emulator.setEnableExceptions(true);
+//                        descriptor.setFeatureAttributeEmulator(emulator);
+//                        this.pickerVirtualField.getPreviewSymbolTable().value(descriptor.getName());
+//                    } catch(Throwable ex) {
+//                        emulator.setEnableExceptions(false);
+//                        descriptor.setFeatureAttributeEmulator(null);
+//                        ThreadSafeDialogsManager dialogs = ToolsSwingLocator.getThreadSafeDialogsManager();
+//                        dialogs.messageDialog(
+//                                i18n.getTranslation("_The_expression_of_virtual_field_is_not_valid")+
+//                                    "\n"+
+//                                    ex.getMessage(), 
+//                                i18n.getTranslation("_Warning"), 
+//                                JOptionPane.WARNING_MESSAGE
+//                        );
+//                        return null;
+//                    } finally {
+//                        emulator.setEnableExceptions(false);
+//                    }
                 }
             }
-            descriptor.setDataProfileName(
-                StringUtils.defaultIfBlank((String) this.cboDataProfile.getSelectedItem(),null)
-            );
         } catch (Exception ex) {
             LOGGER.warn("Can't retrieve information from user form.",ex);
             ThreadSafeDialogsManager dialogs = ToolsSwingLocator.getThreadSafeDialogsManager();
Index: trunk/org.gvsig.desktop/org.gvsig.desktop.compat.cdc/org.gvsig.fmap.dal/org.gvsig.fmap.dal.api/src/main/java/org/gvsig/fmap/dal/expressionevaluator/FeatureAttributeEmulatorExpression.java
===================================================================
--- trunk/org.gvsig.desktop/org.gvsig.desktop.compat.cdc/org.gvsig.fmap.dal/org.gvsig.fmap.dal.api/src/main/java/org/gvsig/fmap/dal/expressionevaluator/FeatureAttributeEmulatorExpression.java	(revision 44153)
+++ trunk/org.gvsig.desktop/org.gvsig.desktop.compat.cdc/org.gvsig.fmap.dal/org.gvsig.fmap.dal.api/src/main/java/org/gvsig/fmap/dal/expressionevaluator/FeatureAttributeEmulatorExpression.java	(revision 44154)
@@ -19,4 +19,8 @@
 
     public boolean isValid();
     
+    public void setEnableExceptions(boolean enableException);
+    
+    public boolean isEnableExceptions();
+    
 }
Index: trunk/org.gvsig.desktop/org.gvsig.desktop.compat.cdc/org.gvsig.fmap.dal/org.gvsig.fmap.dal.impl/src/main/java/org/gvsig/fmap/dal/impl/expressionevaluator/DefaultFeatureAttributeEmulatorExpression.java
===================================================================
--- trunk/org.gvsig.desktop/org.gvsig.desktop.compat.cdc/org.gvsig.fmap.dal/org.gvsig.fmap.dal.impl/src/main/java/org/gvsig/fmap/dal/impl/expressionevaluator/DefaultFeatureAttributeEmulatorExpression.java	(revision 44153)
+++ trunk/org.gvsig.desktop/org.gvsig.desktop.compat.cdc/org.gvsig.fmap.dal/org.gvsig.fmap.dal.impl/src/main/java/org/gvsig/fmap/dal/impl/expressionevaluator/DefaultFeatureAttributeEmulatorExpression.java	(revision 44154)
@@ -46,6 +46,8 @@
     private boolean valid;
     private String errorMessage;
     
+    private boolean enableExceptions;
+    
     private FilteredLogger logger;
     
     public DefaultFeatureAttributeEmulatorExpression() {
@@ -54,6 +56,7 @@
     public DefaultFeatureAttributeEmulatorExpression(FeatureType featureType, Expression expression) {
         this.expression = expression;
         this.checkVars(featureType);
+        this.enableExceptions = false;
     }
 
     private FilteredLogger getLogger() {
@@ -95,6 +98,14 @@
         this.requiredFields = theUsedFields.toArray(new String[theUsedFields.size()]);
     }
 
+    public boolean isEnableExceptions() {
+        return enableExceptions;
+    }
+
+    public void setEnableExceptions(boolean enableExceptions) {
+        this.enableExceptions = enableExceptions;
+    }
+
     @Override
     public SymbolTable getSymbolTable() {
         if (this.symbolTable == null) {
@@ -130,6 +141,9 @@
             Object result = this.expression.execute(this.getSymbolTable());
             return result;
         } catch(Exception ex) {
+            if( this.enableExceptions ) {
+                throw ex;
+            }
             this.getLogger().warn("Problems evaluating expression '"+this.expression.getPhrase()+"' with feature '"+feature.getReference().toString()+"'.", ex);
             return null;
         }
Index: trunk/org.gvsig.desktop/org.gvsig.desktop.plugin/org.gvsig.app/org.gvsig.app.mainplugin/src/main/resources-plugin/i18n/text.properties
===================================================================
--- trunk/org.gvsig.desktop/org.gvsig.desktop.plugin/org.gvsig.app/org.gvsig.app.mainplugin/src/main/resources-plugin/i18n/text.properties	(revision 44153)
+++ trunk/org.gvsig.desktop/org.gvsig.desktop.plugin/org.gvsig.app/org.gvsig.app.mainplugin/src/main/resources-plugin/i18n/text.properties	(revision 44154)
@@ -1358,3 +1358,5 @@
 _Tip=Sugerencia
 _Cant_retrieve_information_from_user_form=No se puede recuperar informaci\u00f3n del formulario de usuario.
 _See_log_for_details=Ver el registro para m\u00e1s detalles.
+_The_expression_of_virtual_field_is_not_valid=La expresi\u00f3n del campo virtual no es valida.
+_The_expression_contains_a_reference_to_itself=La expresi\u00f3n tiene una referencia a ella misma.
Index: trunk/org.gvsig.desktop/org.gvsig.desktop.plugin/org.gvsig.app/org.gvsig.app.mainplugin/src/main/resources-plugin/i18n/text_en.properties
===================================================================
--- trunk/org.gvsig.desktop/org.gvsig.desktop.plugin/org.gvsig.app/org.gvsig.app.mainplugin/src/main/resources-plugin/i18n/text_en.properties	(revision 44153)
+++ trunk/org.gvsig.desktop/org.gvsig.desktop.plugin/org.gvsig.app/org.gvsig.app.mainplugin/src/main/resources-plugin/i18n/text_en.properties	(revision 44154)
@@ -1276,4 +1276,6 @@
 _Message=Message
 _Tip=Tip
 _Cant_retrieve_information_from_user_form=Can't retrieve information from user form.
-_See_log_for_details=See log for details.
\ No newline at end of file
+_See_log_for_details=See log for details.
+_The_expression_of_virtual_field_is_not_valid=The expression of virtual field es not valid.
+_The_expression_contains_a_reference_to_itself=The expression contains a reference to itself.
Index: trunk/org.gvsig.desktop/org.gvsig.desktop.library/org.gvsig.expressionevaluator/org.gvsig.expressionevaluator.lib/org.gvsig.expressionevaluator.lib.impl/src/main/java/org/gvsig/expressionevaluator/impl/DefaultInterpreter.java
===================================================================
--- trunk/org.gvsig.desktop/org.gvsig.desktop.library/org.gvsig.expressionevaluator/org.gvsig.expressionevaluator.lib/org.gvsig.expressionevaluator.lib.impl/src/main/java/org/gvsig/expressionevaluator/impl/DefaultInterpreter.java	(revision 44153)
+++ trunk/org.gvsig.desktop/org.gvsig.desktop.library/org.gvsig.expressionevaluator/org.gvsig.expressionevaluator.lib/org.gvsig.expressionevaluator.lib.impl/src/main/java/org/gvsig/expressionevaluator/impl/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;
     }
 
Index: trunk/org.gvsig.desktop/org.gvsig.desktop.library/org.gvsig.expressionevaluator/org.gvsig.expressionevaluator.lib/org.gvsig.expressionevaluator.lib.impl/src/main/java/org/gvsig/expressionevaluator/impl/I18N.java
===================================================================
--- trunk/org.gvsig.desktop/org.gvsig.desktop.library/org.gvsig.expressionevaluator/org.gvsig.expressionevaluator.lib/org.gvsig.expressionevaluator.lib.impl/src/main/java/org/gvsig/expressionevaluator/impl/I18N.java	(revision 44153)
+++ trunk/org.gvsig.desktop/org.gvsig.desktop.library/org.gvsig.expressionevaluator/org.gvsig.expressionevaluator.lib/org.gvsig.expressionevaluator.lib.impl/src/main/java/org/gvsig/expressionevaluator/impl/I18N.java	(revision 44154)
@@ -148,5 +148,9 @@
         return "The operator 'less than or equal' is '<='.";
     }
 
+    static String Maximum_recursion_limit_exceeded() {
+        return "Maximum recursion limit exceeded.";
+    }
 
+
 }
Index: trunk/org.gvsig.desktop/org.gvsig.desktop.library/org.gvsig.expressionevaluator/org.gvsig.expressionevaluator.lib/org.gvsig.expressionevaluator.lib.impl/src/main/java/org/gvsig/expressionevaluator/impl/DefaultCodeBuilder.java
===================================================================
--- trunk/org.gvsig.desktop/org.gvsig.desktop.library/org.gvsig.expressionevaluator/org.gvsig.expressionevaluator.lib/org.gvsig.expressionevaluator.lib.impl/src/main/java/org/gvsig/expressionevaluator/impl/DefaultCodeBuilder.java	(revision 44153)
+++ trunk/org.gvsig.desktop/org.gvsig.desktop.library/org.gvsig.expressionevaluator/org.gvsig.expressionevaluator.lib/org.gvsig.expressionevaluator.lib.impl/src/main/java/org/gvsig/expressionevaluator/impl/DefaultCodeBuilder.java	(revision 44154)
@@ -113,12 +113,49 @@
 
     }
 
-    public class BaseIdentifier extends BaseCode implements Identifier {
+    public interface RecursionControlSupport {
+        
+        public boolean enterCode(int max);
+        
+        public void exitCode();
+        
+        public void resetRecursionState();
+    }
+    
+    private class RecursionSupport implements RecursionControlSupport {
+    
+        private int counter;
+        
+        public RecursionSupport() {
+            this.counter = 0;
+        }
 
-        String name;
+        @Override
+        public boolean enterCode(int max) {
+            this.counter += 1;
+            return this.counter < max;
+        }
 
+        @Override
+        public void exitCode() {
+            this.counter -= 1;
+        }
+
+        @Override
+        public void resetRecursionState() {
+            this.counter = 0;
+        }
+        
+    }
+    
+    public class BaseIdentifier extends BaseCode implements Identifier, RecursionControlSupport {
+
+        private String name;
+        private RecursionSupport recursionSupport;
+
         public BaseIdentifier(String name) {
             this.name = name;
+            this.recursionSupport = new RecursionSupport();
         }
 
         @Override
@@ -140,6 +177,21 @@
             return builder.toString();
         }
 
+        @Override
+        public boolean enterCode(int max) {
+            return this.recursionSupport.enterCode(max);
+        }
+
+        @Override
+        public void exitCode() {
+            this.recursionSupport.exitCode();
+        }
+
+        @Override
+        public void resetRecursionState() {
+            this.recursionSupport.resetRecursionState();
+        }
+
     }
 
     public class BaseCodes implements Codes {
@@ -212,18 +264,20 @@
 
     }
 
-    public class BaseCaller extends BaseCode implements Caller {
+    public class BaseCaller extends BaseCode implements Caller, RecursionControlSupport {
 
         private final String name;
         private final Codes args;
         private Function function;
         private final int type;
+        private RecursionSupport recursionSupport;
 
         public BaseCaller(String name, int type, Codes args) {
             this.name = name;
             this.args = args;
             this.type = type;
             this.function = null;
+            this.recursionSupport = new RecursionSupport();
         }
 
         @Override
@@ -282,6 +336,20 @@
             return builder.toString();
         }
 
+        @Override
+        public boolean enterCode(int max) {
+            return this.recursionSupport.enterCode(max);
+        }
+
+        @Override
+        public void exitCode() {
+            this.recursionSupport.exitCode();
+        }
+
+        @Override
+        public void resetRecursionState() {
+            this.recursionSupport.resetRecursionState();
+        }
     }
 
     public class BaseMethod extends BaseCaller implements Method {
