Index: DefaultJExpressionBuilder.java
===================================================================
--- DefaultJExpressionBuilder.java	(revision 44097)
+++ DefaultJExpressionBuilder.java	(revision 44098)
@@ -1,6 +1,7 @@
 package org.gvsig.expressionevaluator.swing.impl;
 
 import java.awt.BorderLayout;
+import java.awt.Color;
 import java.awt.Dimension;
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
@@ -11,7 +12,6 @@
 import java.net.URL;
 import java.util.ArrayList;
 import java.util.Collection;
-import java.util.Date;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Objects;
@@ -25,6 +25,11 @@
 import javax.swing.JScrollPane;
 import javax.swing.JTree;
 import javax.swing.SwingUtilities;
+import javax.swing.Timer;
+import javax.swing.event.CaretEvent;
+import javax.swing.event.CaretListener;
+import javax.swing.event.DocumentEvent;
+import javax.swing.event.DocumentListener;
 import javax.swing.event.ListSelectionEvent;
 import javax.swing.event.ListSelectionListener;
 import javax.swing.event.TreeModelEvent;
@@ -36,11 +41,13 @@
 import javax.swing.tree.TreePath;
 import org.apache.commons.io.FilenameUtils;
 import org.apache.commons.lang3.StringUtils;
-import org.apache.commons.lang.StringEscapeUtils;
 import org.gvsig.expressionevaluator.Expression;
-import org.gvsig.expressionevaluator.ExpressionBuilder;
 import org.gvsig.expressionevaluator.ExpressionEvaluatorLocator;
+import org.gvsig.expressionevaluator.ExpressionEvaluatorManager;
+import org.gvsig.expressionevaluator.ExpressionRuntimeException;
+import org.gvsig.expressionevaluator.ExpressionSyntaxException;
 import org.gvsig.expressionevaluator.Function;
+import org.gvsig.expressionevaluator.MutableSymbolTable;
 import org.gvsig.expressionevaluator.SymbolTable;
 import org.gvsig.expressionevaluator.swing.Element;
 import org.gvsig.expressionevaluator.swing.Element.CalculatedElement;
@@ -49,12 +56,9 @@
 import org.gvsig.expressionevaluator.swing.Element.GroupElement;
 import org.gvsig.expressionevaluator.swing.Element.SimpleElement;
 import org.gvsig.expressionevaluator.swing.Element.VariableElement;
-import org.gvsig.expressionevaluator.swing.ElementFactory;
 import org.gvsig.expressionevaluator.swing.ExpressionBuilderConfig;
 import org.gvsig.expressionevaluator.swing.ExpressionEvaluatorSwingManager;
 import org.gvsig.expressionevaluator.swing.JExpressionBuilder;
-import org.gvsig.fmap.geom.Geometry;
-import org.gvsig.fmap.geom.primitive.Envelope;
 import org.gvsig.tools.ToolsLocator;
 import org.gvsig.tools.i18n.I18nManager;
 import org.gvsig.tools.script.Script;
@@ -76,6 +80,8 @@
         extends DefaultJExpressionBuilderView
         implements JExpressionBuilder {
 
+    private static final Color COLOR_ERROR = new Color(255,225,232);
+
     private final ExpressionEvaluatorSwingManager manager;
     private GroupElement currentGroup;
     private PickerController<List<Script>> scriptPicker;
@@ -85,10 +91,12 @@
     private ElementsTreeModel treeModel;
     private java.awt.Component infoPanel = null;
     private JEditorPane txtDescription;
+    private Timer timerExpression;
     private ImageIcon defaultIconGroup;
     private ImageIcon defaultIconElement;
     private JScrollPane scrDescription;
-    
+    private Color color_ok;
+
     private class ElementsTreeModel implements TreeModel {
 
         private final GroupElement root;
@@ -207,9 +215,29 @@
         ToolsSwingManager toolsSwingManager = ToolsSwingLocator.getToolsSwingManager();
         final IconTheme theme = ToolsSwingLocator.getIconThemeManager().getCurrent();
         
+        this.btnTip.setVisible(false);
+        this.btnTip.addActionListener(new ActionListener() {
+            @Override
+            public void actionPerformed(ActionEvent e) {
+                ThreadSafeDialogsManager dialogs = ToolsSwingLocator.getThreadSafeDialogsManager();
+                dialogs.messageDialog(
+                        btnTip.getToolTipText(), 
+                        "Tip", 
+                        JOptionPane.INFORMATION_MESSAGE
+                );
+            }
+        });
+
         this.defaultIconGroup = theme.get("expressionbuilder-element-group");
         this.defaultIconElement = theme.get("expressionbuilder-element");
         
+        this.timerExpression = new Timer(3000, new ActionListener() {
+            @Override
+            public void actionPerformed(ActionEvent e) {
+                doCheckExpression();
+            }
+        });
+        this.timerExpression.setRepeats(false);
         this.txtDescription = new JEditorPane();
         this.scrDescription = new JScrollPane(this.txtDescription);
         
@@ -228,7 +256,37 @@
         });
         this.pnlDescription.setLayout(new BorderLayout());
         this.pnlDescription.setBackground(this.txtDescription.getBackground());
-        
+        this.color_ok = this.txtExpression.getBackground();
+        this.txtExpression.addCaretListener(new CaretListener() {
+            @Override
+            public void caretUpdate(CaretEvent e) {
+                lblColumn.setText(Integer.toString(e.getDot()));
+            }
+        });
+        this.txtExpression.getDocument().addDocumentListener(new DocumentListener() {
+            @Override
+            public void insertUpdate(DocumentEvent e) {
+                timerExpression.restart();
+            }
+
+            @Override
+            public void removeUpdate(DocumentEvent e) {
+                timerExpression.restart();
+            }
+
+            @Override
+            public void changedUpdate(DocumentEvent e) {
+                timerExpression.restart();
+            }
+        });
+        this.txtExpression.addKeyListener(new KeyAdapter() {
+            @Override
+            public void keyPressed(KeyEvent e) {
+                   if( e.getKeyCode()==KeyEvent.VK_C && e.isControlDown() ) {
+                       doCheckExpression();
+                   } 
+            }
+        });
         this.treeElements.setCellRenderer(new DefaultTreeCellRenderer() {
             @Override
             public java.awt.Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) {
@@ -263,6 +321,18 @@
                 }
             }
         });
+        this.treeElements.addMouseListener(new MouseAdapter() {
+            @Override
+            public void mouseClicked(MouseEvent e) {
+                if( e.getButton()==MouseEvent.BUTTON1 && e.getClickCount()==2 ) {
+                    TreePath path = treeElements.getSelectionPath();
+                    Element element = (Element) path.getLastPathComponent();
+                    if( element instanceof SimpleElement ) {
+                        doInsert((SimpleElement) element);
+                    }
+                }
+            }
+        });
         this.btnGroupElementInsert.addActionListener(new ActionListener() {
             @Override
             public void actionPerformed(ActionEvent e) {
@@ -307,6 +377,7 @@
                         label.setIcon(element.getIcon());
                     }
                     label.setText(element.getName());
+                    label.setEnabled(element.isEnabled());
                 } else {
                     label.setIcon(null);
                     label.setText(Objects.toString(value, ""));
@@ -439,6 +510,56 @@
         this.txtDescription.setPreferredSize(new Dimension(200,200));
     }
 
+    private void doCheckExpression() {
+        Expression expression = this.getExpression();
+        this.btnTip.setVisible(false);
+        if( expression == null ) {
+            this.lblMsg.setText("");
+            this.txtExpression.setBackground(this.color_ok);
+            return;
+        }
+        try {
+            expression.getCode();
+            this.lblMsg.setText("");
+            this.txtExpression.setBackground(this.color_ok);
+        } catch(ExpressionSyntaxException ex) {
+            this.lblMsg.setText("<html>"+ex.getDescription()+"</html>");
+            this.txtExpression.setBackground(COLOR_ERROR);
+            return;
+        } catch(Exception ex) {
+            this.lblMsg.setText("Syntax error in expression");
+            this.txtExpression.setBackground(COLOR_ERROR);
+            return;
+        }
+        if( this.config.getPreviewSymbolTable()==null ) {
+            try {
+                ExpressionEvaluatorManager theManager = ExpressionEvaluatorLocator.getManager();
+                MutableSymbolTable symbolTable = theManager.createSymbolTable();
+                Object value = expression.execute(symbolTable);
+                this.lblMsg.setText("<html>"+StringUtils.abbreviate(Utils.toString(value),100)+"</html>");
+                this.txtExpression.setBackground(this.color_ok);
+            } catch(Exception ex) {
+                this.lblMsg.setText("");
+            }
+        } else {
+            try {
+                Object value = expression.execute(this.config.getPreviewSymbolTable());
+                this.lblMsg.setText("<html>"+StringUtils.abbreviate(Utils.toString(value),100)+"</html>");
+                this.txtExpression.setBackground(this.color_ok);
+            } catch(ExpressionRuntimeException ex) {
+                if( !StringUtils.isBlank(ex.getTip()) ) {
+                    this.btnTip.setToolTipText(ex.getTip());
+                    this.btnTip.setVisible(true);
+                }
+                this.lblMsg.setText("<html>Runtime error. "+ex.getMessage()+"</html>");
+                this.txtExpression.setBackground(COLOR_ERROR);
+            } catch(Exception ex) {
+                this.lblMsg.setText("<html>Runtime error. "+ex.getMessage()+"</html>");
+                this.txtExpression.setBackground(COLOR_ERROR);
+            }
+        }
+    }
+    
     private String removeCursorMark(String s) {
         s = s.replace("{{", "");
         s = s.replace("}}", "");
@@ -474,7 +595,7 @@
             }
         } else if( element instanceof ConstantElement) {
             Object value = ((ConstantElement) element).getValue();
-            doInsert(" "+repr(value)+" ");
+            doInsert(" "+Utils.repr(value)+" ");
         } else {
             doInsert(" "+element.getName()+" ");
         }
@@ -486,6 +607,10 @@
     }
 
     private void doSelectElement(SimpleElement element) {
+        if( !element.isEnabled() ) {
+            this.cleanDescription();
+            return;
+        }
         I18nManager i18n = ToolsLocator.getI18nManager();
         
         Component aditionalPanel = element.getAditionalPanel();
@@ -500,7 +625,7 @@
                     html.append("<b>").append(i18n.getTranslation("_Value")).append("</b> NULL<br>\n");
                 } else {
                     html.append("<b>").append(i18n.getTranslation("_Constant")).append("</b><br>\n<br>\n");
-                    html.append("<b>").append(i18n.getTranslation("_Value")).append("</b> ").append(repr(value)).append("<br>\n");
+                    html.append("<b>").append(i18n.getTranslation("_Value")).append("</b> ").append(Utils.repr(value)).append("<br>\n");
                     html.append("<b>").append(i18n.getTranslation("_Type")).append("</b> ").append(value.getClass().getSimpleName()).append("<br>\n");
                 }
 
@@ -565,12 +690,7 @@
         
     }
 
-    private void updateSimpleElementsList(GroupElement group) {
-        FilteredListModel model = new FilteredListModel();
-        model.addElement("Loading...");
-        this.currentGroup = group;
-        this.txtGroupElement.setText(group.getName());
-        this.txtSimpleElementFilter.setText("");
+    private void cleanDescription() {
         this.txtDescription.setText("");
         if( this.infoPanel != this.scrDescription ) {
             this.pnlDescription.removeAll();
@@ -579,6 +699,15 @@
             this.pnlDescription.repaint();
             this.infoPanel = this.scrDescription;
         }
+    }
+    
+    private void updateSimpleElementsList(GroupElement group) {
+        FilteredListModel model = new FilteredListModel();
+        model.addElement("Loading...");
+        this.currentGroup = group;
+        this.txtGroupElement.setText(group.getName());
+        this.txtSimpleElementFilter.setText("");
+        this.cleanDescription();
         if (group instanceof SimpleElement) {
             this.btnGroupElementInsert.setEnabled(true);
         } else {
@@ -705,6 +834,16 @@
     }
 
     @Override
+    public SymbolTable getPreviewSymbolTable() {
+        return this.config.getPreviewSymbolTable();
+    }
+
+    @Override
+    public void setPreviewSymbolTable(SymbolTable symbolTable) {
+        this.config.setPreviewSymbolTable(symbolTable);
+    }
+
+    @Override
     public void setScriptsEnabled(boolean enabled) {
         this.config.setScriptsEnabled(enabled);
         this.tabExpressionBuilder.setEnabledAt(0, this.config.getScriptsEnabled());
@@ -729,29 +868,6 @@
         }
         return this;
     }
-
-    private String repr(Object value)  {
-        if (value == null) {
-            return "NULL";
-        } else if (value instanceof Date) {
-            return "DATE('" + value + "')";
-        } else if (value instanceof Boolean) {
-            return value.toString();
-        } else if (value instanceof String) {
-            return "'" + StringEscapeUtils.escapeSql((String) value) + "'";
-        } else if (value instanceof Geometry) {
-            ExpressionBuilder builder = ExpressionEvaluatorLocator.getManager().createExpressionBuilder();
-            return builder.geometry((Geometry)value).toString();
-        } else if (value instanceof Envelope) {
-            ExpressionBuilder builder = ExpressionEvaluatorLocator.getManager().createExpressionBuilder();
-            return builder.envelope((Envelope)value).toString();
-        } else if (value instanceof Number) {
-            return ((Number)value).toString();
-        } else {
-            return StringEscapeUtils.escapeSql(value.toString());
-        }
-        
-    }
     
     private void doSetTimeLimit() {
         ThreadSafeDialogsManager dialogs = ToolsSwingLocator.getThreadSafeDialogsManager();
