Statistics
| Revision:

svn-gvsig-desktop / trunk / org.gvsig.desktop / org.gvsig.desktop.library / org.gvsig.expressionevaluator / org.gvsig.expressionevaluator.swing / org.gvsig.expressionevaluator.swing.impl / src / main / java / org / gvsig / expressionevaluator / swing / impl / DefaultJExpressionBuilder.java @ 46172

History | View | Annotate | Download (39.3 KB)

1
package org.gvsig.expressionevaluator.swing.impl;
2

    
3
import java.awt.BorderLayout;
4
import java.awt.Dimension;
5
import java.awt.Toolkit;
6
import java.awt.datatransfer.Clipboard;
7
import java.awt.datatransfer.StringSelection;
8
import java.awt.event.ActionEvent;
9
import java.awt.event.ActionListener;
10
import java.awt.event.KeyAdapter;
11
import java.awt.event.KeyEvent;
12
import java.awt.event.MouseAdapter;
13
import java.awt.event.MouseEvent;
14
import java.io.InputStream;
15
import java.net.URL;
16
import java.util.ArrayList;
17
import java.util.Collection;
18
import java.util.HashSet;
19
import java.util.List;
20
import java.util.Locale;
21
import java.util.Map;
22
import java.util.Objects;
23
import java.util.function.Function;
24
import java.util.function.Predicate;
25
import javax.swing.DefaultListCellRenderer;
26
import javax.swing.ImageIcon;
27
import javax.swing.JComponent;
28
import javax.swing.JEditorPane;
29
import javax.swing.JLabel;
30
import javax.swing.JList;
31
import javax.swing.JMenuItem;
32
import javax.swing.JOptionPane;
33
import javax.swing.JPanel;
34
import javax.swing.JPopupMenu;
35
import javax.swing.JScrollPane;
36
import javax.swing.JTabbedPane;
37
import javax.swing.JTree;
38
import javax.swing.SwingUtilities;
39
import javax.swing.event.CaretEvent;
40
import javax.swing.event.CaretListener;
41
import javax.swing.event.ListSelectionEvent;
42
import javax.swing.event.ListSelectionListener;
43
import javax.swing.event.TreeModelEvent;
44
import javax.swing.event.TreeModelListener;
45
import javax.swing.event.TreeSelectionEvent;
46
import javax.swing.event.TreeSelectionListener;
47
import javax.swing.text.JTextComponent;
48
import javax.swing.tree.DefaultTreeCellRenderer;
49
import javax.swing.tree.TreeModel;
50
import javax.swing.tree.TreePath;
51
import org.apache.commons.io.IOUtils;
52
import org.apache.commons.lang3.StringUtils;
53
import org.gvsig.configurableactions.ConfigurableActionsMamager;
54
import org.gvsig.expressionevaluator.Expression;
55
import org.gvsig.expressionevaluator.ExpressionEvaluatorLocator;
56
import org.gvsig.expressionevaluator.ExpressionEvaluatorManager;
57
import org.gvsig.expressionevaluator.SymbolTable;
58
import org.gvsig.expressionevaluator.swing.Element;
59
import org.gvsig.expressionevaluator.swing.Element.GroupElement;
60
import org.gvsig.expressionevaluator.swing.Element.SimpleElement;
61
import org.gvsig.expressionevaluator.swing.ExpressionBuilderConfig;
62
import org.gvsig.expressionevaluator.swing.ExpressionEvaluatorSwingManager;
63
import org.gvsig.expressionevaluator.swing.JExpressionBuilder;
64
import org.gvsig.tools.ToolsLocator;
65
import org.gvsig.tools.i18n.I18nManager;
66
import org.gvsig.tools.script.Script;
67
import org.gvsig.tools.swing.api.Component;
68
import org.gvsig.tools.swing.api.ToolsSwingLocator;
69
import org.gvsig.tools.swing.api.ToolsSwingManager;
70
import org.gvsig.tools.swing.api.bookmarkshistory.ActionEventWithCurrentValue;
71
import static org.gvsig.tools.swing.api.bookmarkshistory.ActionEventWithCurrentValue.ID_GETVALUE;
72
import static org.gvsig.tools.swing.api.bookmarkshistory.ActionEventWithCurrentValue.ID_SETVALUE;
73
import org.gvsig.tools.swing.api.bookmarkshistory.BookmarksController;
74
import org.gvsig.tools.swing.api.bookmarkshistory.HistoryController;
75
import org.gvsig.tools.swing.api.pickercontroller.PickerController;
76
import org.gvsig.tools.swing.api.script.ScriptSwingManager;
77
import org.gvsig.tools.swing.api.threadsafedialogs.ThreadSafeDialogsManager;
78
import org.gvsig.tools.swing.icontheme.IconTheme;
79
import org.gvsig.tools.util.Factory;
80
import org.gvsig.tools.util.ToolsUtilLocator;
81

    
82
/**
83
 *
84
 * @author jjdelcerro
85
 */
86
@SuppressWarnings("UseSpecificCatch")
87
public class DefaultJExpressionBuilder
88
//        extends DefaultJExpressionBuilderView2
89
        implements JExpressionBuilder {
90

    
91
    private final ExpressionEvaluatorSwingManager manager;
92
    private Element currentElement;
93
    private PickerController<List<Script>> scriptPicker;
94
    private ScriptSwingManager.ScriptEditor scriptEditor;
95
    
96
    private final ExpressionBuilderConfig builderConfig;
97
    private ElementsTreeModel treeModel;
98
    private ImageIcon defaultIconGroup;
99
    private ImageIcon defaultIconElement;
100
    private InfoPanel infoPanel;
101
    private AbstractAutomaticExpressionChecker automaticExpressionChecker;
102
    private HistoryController<Expression> historyController;
103
    private BookmarksController<Expression> bookmarksController;
104
    private final DefaultJExpressionBuilderView2 view;
105
    private boolean automaticExpressionCheckerEnabled;
106

    
107
    private static class InfoPanel implements Component {
108

    
109
        private final JEditorPane txtDescription;
110
        private final JScrollPane scrDescription;
111
        private final JTabbedPane tab;
112

    
113
        private final String labelDescription;
114
        private final String labelAdditionalPanel;
115
        private final JPanel pnlContainer;
116
        
117
        private final String defautltDescription;
118

    
119
        private String description; 
120
        private Component additionalPanel;
121
        
122
        public InfoPanel(JPanel pnlContainer, String defautltDescription) {
123
            ToolsSwingManager toolsSwingManager = ToolsSwingLocator.getToolsSwingManager();
124
            I18nManager i18n = ToolsLocator.getI18nManager();
125
            
126
            this.defautltDescription = defautltDescription;
127
            this.pnlContainer = pnlContainer;
128
            this.labelDescription = i18n.getTranslation("_Description");
129
            this.labelAdditionalPanel = i18n.getTranslation("_Assistant");
130
            
131
            this.txtDescription = new JEditorPane();
132
            this.scrDescription = new JScrollPane(this.txtDescription);
133
            this.tab = new JTabbedPane();
134
            
135
            toolsSwingManager.setDefaultPopupMenu(this.txtDescription);
136
            this.txtDescription.setPreferredSize(new Dimension(200,200));
137
        }
138
        
139
        public void clean() {
140
            this.description = null;
141
            this.additionalPanel = null;
142
        }
143
        
144
        public void setDescription(String description) {
145
            this.description = description;
146
        }
147
        
148
        public void setAdditionalPanel(Component panel) {
149
            this.additionalPanel = panel;
150
        }
151

    
152
        @Override
153
        public JComponent asJComponent() {
154
            this.txtDescription.setContentType("text/html");
155
            this.txtDescription.setText(this.description);
156
            this.txtDescription.setCaretPosition(0);
157

    
158
            if( StringUtils.isBlank(this.description) ) {
159
                if( this.additionalPanel==null ) {
160
                    // Sin descripcion ni panel adicional, mostramos
161
                    // la descripcion por defecto.
162
                    this.txtDescription.setText(this.defautltDescription);
163
                    this.txtDescription.setCaretPosition(0);
164
                    return this.scrDescription;
165
                }
166
                // Sin descripcion pero con panel adicional, mostramos el 
167
                // panel adicional.
168
                return this.additionalPanel.asJComponent();
169
            }
170
            if( this.additionalPanel==null ) {
171
                // Con descripcion y sin panel adicional, mostramos
172
                // la descripcion.
173
                return this.scrDescription;                
174
            } 
175
            // Con descripcion y panel adicional, mostramos un tab con los dos.
176
            this.tab.removeAll();
177
            this.tab.add(
178
                    this.labelDescription, 
179
                    this.scrDescription
180
            );
181
            this.tab.add(
182
                    this.labelAdditionalPanel, 
183
                    this.additionalPanel.asJComponent()
184
            );
185
            this.tab.setSelectedIndex(1);
186
            return this.tab;
187
        }
188

    
189
        public void repaint() {
190
            this.pnlContainer.removeAll();
191
            this.pnlContainer.add(this.asJComponent(), BorderLayout.CENTER);
192
            this.pnlContainer.revalidate();
193
            this.pnlContainer.repaint();
194
        }
195
    }
196

    
197
    private class ElementsTreeModel implements TreeModel {
198

    
199
        private final GroupElement root;
200
        private final HashSet<TreeModelListener> listeners;
201

    
202
        public ElementsTreeModel(GroupElement root) {
203
            this.root = root;
204
            this.listeners = new HashSet<>();
205
        }
206

    
207
        @Override
208
        public Object getRoot() {
209
            return this.root;
210
        }
211

    
212
        public void reload() {
213
            this.root.reload();
214
            this.fireTreeChanged();
215
        }
216
        
217
        private List<Element> getChilds(final GroupElement node) {
218
            List<Element> x = new ArrayList<>();
219
            for (Element element : node.getElements()) {
220
//                if (element instanceof GroupElement) {
221
                    x.add(element);
222
//                }
223
            }
224
            return x; //node.getElements();
225
        }
226

    
227
        @Override
228
        public Object getChild(Object parent, int index) {
229
            List<Element> childs = getChilds((GroupElement) parent);
230
            Element child = childs.get(index);
231
            return child;
232
        }
233

    
234
        @Override
235
        public int getChildCount(Object parent) {
236
            List<Element> childs = getChilds((GroupElement) parent);
237
            return childs.size();
238
        }
239

    
240
        @Override
241
        public boolean isLeaf(Object node) {
242
            return !(node instanceof GroupElement);
243
        }
244

    
245
        @Override
246
        public void valueForPathChanged(TreePath path, Object newValue) {
247
        }
248

    
249
        @Override
250
        public int getIndexOfChild(Object parent, Object child) {
251
            List<Element> childs = getChilds((GroupElement) parent);
252
            int n = childs.indexOf(child);
253
            return n;
254
        }
255

    
256
        @Override
257
        public void addTreeModelListener(TreeModelListener l) {
258
            this.listeners.add(l);
259
        }
260

    
261
        @Override
262
        public void removeTreeModelListener(TreeModelListener l) {
263
            this.listeners.remove(l);
264
        }
265

    
266
        protected void fireTreeChanged() {
267
            for (TreeModelListener listener : listeners) {
268
                TreeModelEvent e = new TreeModelEvent(this, new Object[] {this.root});
269
                listener.treeNodesChanged(e);
270
            }
271
        }
272
    }
273
    
274
    public DefaultJExpressionBuilder(
275
            ExpressionEvaluatorSwingManager manager, 
276
            ExpressionBuilderConfig config,
277
            DefaultJExpressionBuilderView2 view
278
      ) {
279
        this.automaticExpressionCheckerEnabled = true;
280
        this.manager = manager;
281
        if( config == null ) {
282
            this.builderConfig = new DefaultExpressionBuilderConfig(manager);
283
        } else {
284
            this.builderConfig = config;
285
        }
286
        this.view = view;
287
        
288
        this.initComponents();
289
    }
290
    
291
    @SuppressWarnings("Convert2Lambda")
292
    private void initComponents() {
293
        ConfigurableActionsMamager cfgActionsManager = ToolsUtilLocator.getConfigurableActionsMamager();
294
        JComponent c = cfgActionsManager.getConfigurableActionsComponent(CONFIGURABLE_PANEL_ID, this);
295
        this.view.pnlCfgActions.setLayout(new BorderLayout(0,0));
296
        this.view.pnlCfgActions.add(c, BorderLayout.LINE_END);
297
        
298
        final ToolsSwingManager toolsSwingManager = ToolsSwingLocator.getToolsSwingManager();
299
        final IconTheme theme = ToolsSwingLocator.getIconThemeManager().getCurrent();
300
        final I18nManager i18n = ToolsLocator.getI18nManager();
301
        
302
        this.infoPanel = new InfoPanel(this.view.pnlDescription, load_description_from_resource());
303
        
304
        this.view.btnTip.setVisible(false);
305
        this.view.btnTip.addActionListener(new ActionListener() {
306
            @Override
307
            public void actionPerformed(ActionEvent e) {
308
                ThreadSafeDialogsManager dialogs = ToolsSwingLocator.getThreadSafeDialogsManager();
309
                dialogs.messageDialog(
310
                        view.btnTip.getToolTipText(), 
311
                        "Tip", 
312
                        JOptionPane.INFORMATION_MESSAGE
313
                );
314
            }
315
        });
316

    
317
        this.defaultIconGroup = theme.get("expressionbuilder-element-group");
318
        this.defaultIconElement = theme.get("expressionbuilder-element");
319
        
320
//        toolsSwingManager.translate(this.tabExpressionBuilder);
321
        toolsSwingManager.setDefaultPopupMenu(this.view.txtGroupElement);
322
        toolsSwingManager.setDefaultPopupMenu(this.view.txtSimpleElementFilter);
323
        toolsSwingManager.addClearButton(this.view.txtSimpleElementFilter, new ActionListener() {
324
            @Override
325
            public void actionPerformed(ActionEvent e) {
326
                view.txtSimpleElementFilter.setText("");
327
                doFilter();
328
            }
329
        });
330
        JMenuItem msgMenuTextEditor = new JMenuItem(i18n.getTranslation("text_editor"));
331
        msgMenuTextEditor.addActionListener(new ActionListener() {
332
            @Override
333
            public void actionPerformed(ActionEvent e) {
334
                if( automaticExpressionChecker!=null ) {
335
                  automaticExpressionChecker.showMessageDialog();
336
                }
337
            }
338
        });
339
        JMenuItem msgMenuCopy = new JMenuItem(i18n.getTranslation("copy"));
340
        msgMenuTextEditor.addActionListener(new ActionListener() {
341
            @Override
342
            public void actionPerformed(ActionEvent e) {
343
                if( automaticExpressionChecker!=null ) {
344
                  StringSelection selection = new StringSelection(automaticExpressionChecker.getMessage());
345
                  Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
346
                  clipboard.setContents(selection, selection);                
347
                }
348
            }
349
        });
350
        
351
        JPopupMenu msgMenu = new JPopupMenu();
352
        msgMenu.add(msgMenuTextEditor);
353
        msgMenu.addSeparator();
354
        msgMenu.add(msgMenuCopy);
355
        this.view.lblMsg.setComponentPopupMenu(msgMenu);
356

    
357
        this.view.pnlDescription.setLayout(new BorderLayout());
358
        this.view.getExpressionComponent().addCaretListener(new CaretListener() {
359
            @Override
360
            public void caretUpdate(CaretEvent e) {
361
                view.lblColumn.setText(Integer.toString(e.getDot()));
362
            }
363
        });
364
        this.view.treeElements.setRowHeight(0);
365
        this.view.treeElements.setCellRenderer(new DefaultTreeCellRenderer() {
366
            @Override
367
            public java.awt.Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) {
368
                JLabel component = (JLabel) super.getTreeCellRendererComponent(tree,value, selected, expanded, leaf, row, hasFocus);
369
                JLabel text = component;
370
                JLabel icon = component;
371
                
372
                if( value instanceof Element ) {
373
                    Element element = (Element) value;
374
                    if( element.getIconName()==null ) {
375
                        if( element instanceof GroupElement ) {
376
                            icon.setIcon(defaultIconGroup);
377
                        } else {
378
                            icon.setIcon(defaultIconElement);
379
                        }
380
                    } else {
381
                        icon.setIcon(element.getIcon());
382
                    }
383
                    text.setText(element.getLabel());
384
                    text.setEnabled(element.isEnabled());
385
                } else {
386
                    icon.setIcon(null);
387
                    text.setText(Objects.toString(value, ""));
388
                }
389
                icon.setDisabledIcon(icon.getIcon());
390
                return component;
391
            }
392
        });
393
        
394
        this.view.treeElements.addTreeSelectionListener(new TreeSelectionListener() {
395
            @Override
396
            public void valueChanged(TreeSelectionEvent e) {
397
                TreePath path = e.getPath();
398
                Element element = (Element) path.getLastPathComponent();
399
                doSelectElement(element);
400
                updateSimpleElementsList(element);
401
            }
402
        });
403
        this.view.treeElements.addMouseListener(new MouseAdapter() {
404
            @Override
405
            public void mouseClicked(MouseEvent e) {
406
                if( e.getButton()==MouseEvent.BUTTON1 && e.getClickCount()==2 ) {
407
                    TreePath path = view.treeElements.getSelectionPath();
408
                    if( path!=null ) {
409
                        Element element = (Element) path.getLastPathComponent();
410
                        if( element instanceof SimpleElement ) {
411
                            doInsert((SimpleElement) element);
412
                        }
413
                    }
414
                }
415
            }
416
        });
417
        this.view.btnGroupElementInsert.addActionListener(new ActionListener() {
418
            @Override
419
            public void actionPerformed(ActionEvent e) {
420
                if( currentElement!=null ) {
421
                    doInsert(currentElement.getRenderedValue());
422
                } else {
423
                    doInsert(view.txtGroupElement.getText());
424
                }
425
            }
426
        });
427
        this.view.btnSimpleElementInsert.addActionListener(new ActionListener() {
428
            @Override
429
            public void actionPerformed(ActionEvent e) {
430
                Element element = (Element) view.lstSimpleElement.getSelectedValue();
431
                if (element != null) {
432
                    doInsert(element);
433
                }
434
            }
435
        });
436
        this.view.btnSimpleElementFilter.addActionListener(new ActionListener() {
437
            @Override
438
            public void actionPerformed(ActionEvent e) {
439
                doFilter();
440
            }
441
        });
442
        
443
        this.view.lstSimpleElement.setModel(new FilteredListModel());
444
        this.view.lstSimpleElement.setCellRenderer(new DefaultListCellRenderer() {
445
            @Override
446
            public java.awt.Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
447
                JLabel label = (JLabel)super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
448
                if( value instanceof Element ) {
449
                    Element element = (Element) value;
450
                    if( element.getIconName()==null ) {
451
                        if( element instanceof GroupElement ) {
452
                            label.setIcon(defaultIconGroup);
453
                        } else {
454
                            label.setIcon(defaultIconElement);
455
                        }
456
                    } else {
457
                        label.setIcon(element.getIcon());
458
                    }
459
                    label.setText(element.getLabel());
460
                    label.setEnabled(element.isEnabled());
461
                } else {
462
                    label.setIcon(null);
463
                    label.setText(Objects.toString(value, ""));
464
                }
465
                return label;
466
            }
467
        });
468
        this.view.lstSimpleElement.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
469
            @Override
470
            public void valueChanged(ListSelectionEvent e) {
471
                Element element = (Element) view.lstSimpleElement.getSelectedValue();
472
                if (element != null) {
473
                    doSelectElement(element);
474
                }
475
            }
476
        });
477
        this.view.lstSimpleElement.addMouseListener(new MouseAdapter() {
478
            @Override
479
            public void mouseClicked(MouseEvent e) {
480
                if( e.getButton()==MouseEvent.BUTTON1 && e.getClickCount()==2 ) {
481
                    Element element = (Element) view.lstSimpleElement.getSelectedValue();
482
                    if (element != null) {
483
                        doSelectElement(element);
484
                        doInsert(element);
485
                    }
486
                }
487
            }
488
        });
489
        this.view.lstSimpleElement.addKeyListener(new KeyAdapter() {
490
            @Override
491
            public void keyReleased(KeyEvent e) {
492
                if( e.getKeyCode()==KeyEvent.VK_ENTER ) {
493
                    Element element = (Element) view.lstSimpleElement.getSelectedValue();
494
                    if (element != null) {
495
                        doSelectElement(element);
496
                        doInsert(element);
497
                    }
498
                }
499
            }
500
        });
501

    
502
        this.view.btnSimpleElementTimeLimit.addActionListener(new ActionListener() {
503
            @Override
504
            public void actionPerformed(ActionEvent e) {
505
                doSetTimeLimit();
506
            }
507
        });
508
        this.view.btnSimpleElementSortDown.addActionListener(new ActionListener() {
509
            @Override
510
            public void actionPerformed(ActionEvent e) {
511
                FilteredListModel model = (FilteredListModel) view.lstSimpleElement.getModel();
512
                model.sort(false);
513
            }
514
        });
515
        this.view.btnSimpleElementSortUp.addActionListener(new ActionListener() {
516
            @Override
517
            public void actionPerformed(ActionEvent e) {
518
                FilteredListModel model = (FilteredListModel) view.lstSimpleElement.getModel();
519
                model.sort(true);
520
            }
521
        });
522
        this.view.txtSimpleElementFilter.addKeyListener(new KeyAdapter() {
523
            @Override
524
            public void keyTyped(KeyEvent e) {
525
                if (e.getKeyChar() == '\n') {
526
                    doFilter();
527
                }
528
            }
529
        });
530
        this.view.btnAdd.addActionListener(new ActionListener() {
531
            @Override
532
            public void actionPerformed(ActionEvent e) {
533
                doInsert(" + ");
534
            }
535
        });
536
        this.view.btnDiv.addActionListener(new ActionListener() {
537
            @Override
538
            public void actionPerformed(ActionEvent e) {
539
                doInsert(" / ");
540
            }
541
        });
542
        this.view.btnEq.addActionListener(new ActionListener() {
543
            @Override
544
            public void actionPerformed(ActionEvent e) {
545
                doInsert(" = ");
546
            }
547
        });
548
        this.view.btnMult.addActionListener(new ActionListener() {
549
            @Override
550
            public void actionPerformed(ActionEvent e) {
551
                doInsert(" * ");
552
            }
553
        });
554
        this.view.btnNeq.addActionListener(new ActionListener() {
555
            @Override
556
            public void actionPerformed(ActionEvent e) {
557
                doInsert(" <> ");
558
            }
559
        });
560
        this.view.btnParentClose.addActionListener(new ActionListener() {
561
            @Override
562
            public void actionPerformed(ActionEvent e) {
563
                doInsert(" ) ");
564
            }
565
        });
566
        this.view.btnParentOpen.addActionListener(new ActionListener() {
567
            @Override
568
            public void actionPerformed(ActionEvent e) {
569
                doInsert(" ( ");
570
            }
571
        });
572
        this.view.btnSubst.addActionListener(new ActionListener() {
573
            @Override
574
            public void actionPerformed(ActionEvent e) {
575
                doInsert(" - ");
576
            }
577
        });
578
//        ScriptSwingManager scriptSwingManager = ToolsSwingLocator.getScriptSwingManager();
579
//        this.scriptPicker = scriptSwingManager.createListScriptPickerController(
580
//                cboPickerScripts,
581
//                btnPickerRemove,
582
//                btnPickerSelectScript
583
//        );
584
//        this.scriptEditor = scriptSwingManager.createScriptEditor();
585
//        this.pnlScriptEditorContainer.setLayout(new BorderLayout());
586
//        this.pnlScriptEditorContainer.add(this.scriptEditor.asJComponent(), BorderLayout.CENTER);
587
//    
588
//        this.tabExpressionBuilder.setEnabledAt(0, this.builderConfig.getPreferences().getScriptsEnabled());
589
        
590
        ActionListener historyAndBookmarkListener = new ActionListener() {
591
            @Override
592
            public void actionPerformed(ActionEvent e) {
593
                ActionEventWithCurrentValue<Expression> ee = (ActionEventWithCurrentValue<Expression>)e;
594
                switch(ee.getID()) {
595
                    case ID_SETVALUE:
596
                        setExpression(ee.getCurrentValue());
597
                        break;
598
                    case ID_GETVALUE:
599
                        ee.setCurrentValue(getExpressionWithOutHistory());
600
                        break;
601
                }
602
            }
603
        };
604
        this.historyController = toolsSwingManager.createHistoryController(
605
                ExpressionEvaluatorLocator.getManager().getHistory(), 
606
                view.btnHistory
607
        );
608
        this.historyController.addActionListener(historyAndBookmarkListener);
609
        
610
        this.bookmarksController = toolsSwingManager.createBookmarksController(
611
                ExpressionEvaluatorLocator.getManager().getBookmarks(), 
612
                view.btnBookmarks
613
        );
614
        this.bookmarksController.addActionListener(historyAndBookmarkListener);        
615
        
616
        this.infoPanel.repaint();
617
        
618
        this.view.setPreferredSize(new Dimension(750, 450));
619
        
620

    
621
        Dimension dim = this.view.getPreferredSize();
622
        this.view.spnlExpression.setDividerLocation(dim.height/5);
623
        this.view.spnlBottom.setDividerLocation(dim.width/3);
624
        this.view.spnlItem.setDividerLocation(dim.width/3);
625
    }
626

    
627
    private void message(String text) {
628
      if( StringUtils.isBlank(text) ) {
629
        view.lblMsg.setText("");
630
        return;
631
      }
632
      String msg = text;
633
      String tail = "";
634
      if( StringUtils.contains(msg, "\n") ) {
635
        String[] ss = StringUtils.split(msg, "\n");
636
        if( ss.length>1 ) {
637
          tail = String.format("(%d lines more)", ss.length);
638
          msg = ss[0];
639
        }
640
      }
641
      view.lblMsg.setText(toHTML(StringUtils.abbreviate(msg,70)+tail));      
642
    }
643

    
644
    private String toHTML(String s) {
645
      s = StringUtils.replace(s, "\n", "\n<br>");
646
      s = StringUtils.replace(s, "<html>", "");
647
      s = StringUtils.replace(s, "</html>", "");
648
      s = "<html>"+s+"</html>";
649
      return s;
650
    }
651
    
652
    private String removeCursorMark(String s) {
653
        if( s == null ) {
654
            return null;
655
        }
656
        s = s.replace("{{", "");
657
        s = s.replace("}}", "");
658
        return s;
659
    }
660

    
661
    @Override
662
    public void insertText(String text) {
663
        doInsert(text);
664
    }
665

    
666
    @Override
667
    public String getText() {
668
        return this.view.getExpressionComponent().getText();
669
    }
670
    
671
    @Override
672
    public void setText(String text) {
673
        this.view.getExpressionComponent().setText(text);
674
    }
675
    
676
    private void doInsert(String s) {
677
        int start = s.indexOf("{{");
678
        int end = s.indexOf("}}");
679
        if (start > 0 && end > 0) {
680
          s = s.replace("{{", "");
681
          s = s.replace("}}", "");
682
        }
683
        JTextComponent jtext = this.view.getExpressionComponent();
684
        int selstart = this.view.getExpressionComponent().getSelectionStart();
685
        jtext.replaceSelection(s);
686
        if (start > 0 && end > 0) {
687
            jtext.setSelectionStart(selstart+start);
688
            jtext.setSelectionEnd(selstart+end - 2);
689
        }
690
        jtext.requestFocusInWindow();
691
    }
692

    
693
    private void doInsert(Element element) {
694
        doInsert(element.getRenderedValue());
695
        element.used();
696
    }
697

    
698
    private void doFilter() {
699
        FilteredListModel model = (FilteredListModel) this.view.lstSimpleElement.getModel();
700
        model.setFilter(this.view.txtSimpleElementFilter.getText());
701
    }
702

    
703
    private void doSelectElement(Element element) {
704
//        if( !element.isEnabled() ) {
705
//            this.infoPanel.clean();
706
//        } else {
707
            this.infoPanel.setDescription(removeCursorMark(element.getDescription()));
708
            this.infoPanel.setAdditionalPanel(element.getAditionalPanel(this));
709
            this.infoPanel.repaint();
710
//        }        
711
    }
712

    
713
    private void updateSimpleElementsList(Element element) {
714
        FilteredListModel model = new FilteredListModel();
715
        this.currentElement = element;
716
        this.view.txtSimpleElementFilter.setText("");
717
        if (element instanceof SimpleElement) {
718
            this.view.btnGroupElementInsert.setEnabled(true);
719
        } else {
720
            this.view.btnGroupElementInsert.setEnabled(false);
721
        }
722
        this.view.lstSimpleElement.setEnabled(false);
723
        this.view.lstSimpleElement.setModel(model);
724
        if(element == null){
725
            return;
726
        }
727
        model.addElement("Loading...");
728
        this.view.txtGroupElement.setText(element.getName());
729

    
730
        Thread th = new Thread(() -> {
731
            simpleElementsUpdater();
732
        }, "ExpressionBuilderSimpleElementsUpdater");
733
        th.start();
734
        
735
    }
736

    
737
    private void simpleElementsUpdater() {
738
        final Element element = this.currentElement;
739
        final FilteredListModel model = new FilteredListModel();
740
        for (Element value : element.getValues()) {
741
            if (element != this.currentElement) {
742
                // Como estamos en otro hilo, cuando el usuario cambia de elemento,
743
                // cancelamos las actualizaciones que teniamos en curso.
744
                return;
745
            }
746
            if (value instanceof SimpleElement) {
747
                model.addElement(value);
748
            }
749
        }
750
        model.sort(true);
751
        SwingUtilities.invokeLater(() -> {
752
            if (element != currentElement) {
753
                return;
754
            }
755
            if( element.hasMoreValues()) {
756
                view.lblSimpleElementsMsg.setText("More elements...");
757
            } else {
758
                view.lblSimpleElementsMsg.setText("");
759
            }
760
            view.lstSimpleElement.setEnabled(true);
761
            view.lstSimpleElement.setModel(model);
762
        });
763

    
764
    }
765

    
766
    @Override
767
    public Expression getExpression() {
768
        Expression expression = this.getExpressionWithOutHistory();
769
        ExpressionEvaluatorManager theManager = ExpressionEvaluatorLocator.getManager();
770
        theManager.getHistory().add(expression);
771
        return expression;
772
    }
773
    
774
    @Override
775
    public Expression getExpressionWithOutHistory() {
776
        String phrase = this.view.getExpressionComponent().getText();
777
        if (StringUtils.isEmpty(phrase)) {
778
            return null;
779
        }
780
        Expression expression = ExpressionEvaluatorLocator.getManager().createExpression();
781
        expression.setPhrase(phrase);
782
//        Script script = ToolsLocator.getScriptManager().createScript("UserScript", "", "python");
783
//        this.scriptEditor.fetch(script);
784
//        if( !StringUtils.isEmpty(script.getCode()) ) {
785
//            expression.setUserScript(script);
786
//        }
787
        return expression;
788
    }
789

    
790
    @Override
791
    public void setExpression(Expression expression) {
792
        if( expression==null ) {
793
            this.view.getExpressionComponent().setText("");
794
        } else {
795
            this.view.getExpressionComponent().setText(expression.getPhrase());
796
        }
797
//            try { this.scriptEditor.set(null); } catch(Exception ex) {}
798
//            this.scriptPicker.set(null);
799
//        } else {
800
//            this.txtExpression.setText(expression.getPhrase());
801
//            if( expression.getUserScript()!=null ) {
802
//                this.scriptEditor.set(expression.getUserScript());
803
//            }
804
//            if( expression.getScripts()!=null ) {
805
//                this.scriptPicker.set(expression.getScripts().toList());
806
//            }
807
//        }
808
    }
809

    
810
    @Override
811
    public Preferences getPreferences() {
812
        return this.builderConfig.getPreferences();
813
    }
814
    
815
    @Override
816
    public List<Element> getElements() {
817
        return this.builderConfig.getElements();
818
    }
819
    
820
    @Override
821
    public void addElement(Element element) {
822
        this.builderConfig.addElement(element);
823
    }
824
    
825
    @Override
826
    public Collection<SymbolTable> getSymbolTables() {
827
        return this.builderConfig.getSymbolTables();
828
    }
829
    
830
    @Override
831
    public void addSymbolTable(String name) {
832
        this.builderConfig.addSymbolTable(name);
833
    }
834
    
835
    @Override
836
    public void addSymbolTable(SymbolTable symbolTable) {
837
        this.builderConfig.addSymbolTable(symbolTable);
838
    }
839

    
840
    @Override
841
    public void removeAllSymbolTables() {
842
        this.builderConfig.removeAllSymbolTables();
843
    }
844

    
845
    @Override
846
    public void removeSymbolTable(String name) {
847
        this.builderConfig.removeSymbolTable(name);
848
    }
849

    
850
    @Override
851
    public SymbolTable getPreviewSymbolTable() {
852
        return this.builderConfig.getPreviewSymbolTable();
853
    }
854

    
855
    @Override
856
    public void setPreviewSymbolTable(SymbolTable symbolTable) {
857
        this.builderConfig.setPreviewSymbolTable(symbolTable);
858
    }
859
    
860
    @Override
861
    public void addPreviewSymbolTable(SymbolTable symbolTable) {
862
        this.builderConfig.addPreviewSymbolTable(symbolTable);
863
    }
864

    
865
    @Override
866
    public void setAutomaticExpressionCheckerEnabled(boolean enabled) {
867
      if( this.automaticExpressionChecker==null ) {
868
        this.automaticExpressionCheckerEnabled = enabled;
869
      }
870
    }
871
    
872
    @Override
873
    public boolean isAutomaticExpressionCheckerEnabled() {
874
      return this.automaticExpressionCheckerEnabled;
875
    }
876
    
877
    @Override
878
    public JComponent asJComponent() {
879
      if (this.automaticExpressionChecker == null && this.automaticExpressionCheckerEnabled ) {
880
        this.automaticExpressionChecker = new AbstractAutomaticExpressionChecker(
881
                builderConfig, this.view.getExpressionComponent()
882
        ) {
883
          @Override
884
          protected Expression getExpression() {
885
            return DefaultJExpressionBuilder.this.getExpressionWithOutHistory();
886
          }
887

    
888
          @Override
889
          protected void setMessage(int mode, String text) {
890
            super.setMessage(mode, text);
891
            message(text);
892
          }
893

    
894
          @Override
895
          protected void setPreview(Object value) {
896
            super.setPreview(value);
897
            message(this.getPreview());
898
          }
899

    
900
          @Override
901
          protected void setTip(String theTip) {
902
            super.setTip(theTip);
903
            view.btnTip.setToolTipText(toHTML(this.getTip()));
904
            view.btnTip.setVisible(this.isTipEnabled());
905
          }
906

    
907
          @Override
908
          protected String getSuggestion() {
909
            return builderConfig.getSuggestion(this.getExpression());
910
          }
911

    
912
        };
913

    
914
      }
915
      this.initializeTree();
916
      SwingUtilities.invokeLater(() -> {
917
        view.getExpressionComponent().requestFocusInWindow();
918
      });
919
      return this.view;
920
    }
921

    
922
    private void initializeTree() {
923
        if( this.treeModel == null ) {
924
            I18nManager i18n = ToolsLocator.getI18nManager();
925
            this.treeModel = new ElementsTreeModel( (GroupElement)
926
                    this.manager.createGroupElement(
927
                            i18n.getTranslation("_Elements"),
928
                            this.getElements()
929
                    ).setConfig(this));
930
            this.view.treeElements.setModel(this.treeModel);
931
            for (int i = 0; i < this.view.treeElements.getRowCount(); i++) {
932
                TreePath pathRow = this.view.treeElements.getPathForRow(i);
933
                Element element = (Element) pathRow.getLastPathComponent();
934
                if (i18n.getTranslation("_Functions").equals(element.getName())) {
935
                    this.view.treeElements.setSelectionPath(pathRow);
936
                    doSelectElement(element);
937
                    updateSimpleElementsList(element);
938
                    break;
939
                }
940
            }
941
        }
942
    }
943
    
944
    private void doSetTimeLimit() {
945
        ThreadSafeDialogsManager dialogs = ToolsSwingLocator.getThreadSafeDialogsManager();
946
        String s = dialogs.inputDialog(
947
                "Indicate the time limit in seconds for calculating the elements", 
948
                "Time limit", 
949
                JOptionPane.QUESTION_MESSAGE, 
950
                String.valueOf(this.builderConfig.getPreferences().getSimpleElementsLimit())
951
        );
952
        if( StringUtils.isBlank(s) ) {
953
            return;
954
        }
955
        try {
956
            int n = Integer.parseInt(s);
957
            this.builderConfig.getPreferences().setSimpleElementsLimit(n);
958
            this.treeModel.reload();
959
            updateSimpleElementsList(currentElement);
960
        } catch(Exception ex) {
961
            dialogs.messageDialog(
962
                "Invalid time limit '"+s+"'.", 
963
                "Time limit", 
964
                JOptionPane.WARNING_MESSAGE
965
            );                    
966
        }
967
    }
968

    
969
    private String load_description_from_resource() {
970
        String lang = Locale.getDefault().getLanguage();
971
        URL url = this.getClass().getResource("/org/gvsig/expressionevaluator/swing/jexpressionbuilder/"+lang+"/description.html");
972
        if( url == null ) {
973
            url = this.getClass().getResource("/org/gvsig/expressionevaluator/swing/jexpressionbuilder/en/description.html");
974
            if( url == null ) {
975
                return null;
976
            }
977
        }
978
        InputStream is = null;
979
        try {
980
            is = url.openStream();
981
            List<String> lines = IOUtils.readLines(is);
982
            return StringUtils.join(lines,  "\n");
983
        } catch (Exception ex) {
984
            return null;
985
        } finally {
986
            IOUtils.closeQuietly(is);
987
        }
988
    }
989
    
990
    @Override
991
    public void expandElement(Element element) {
992
        this.initializeTree();
993
        for (int i = 0; i < this.view.treeElements.getRowCount(); i++) {
994
            TreePath path = this.view.treeElements.getPathForRow(i);
995
            if( path.getLastPathComponent()==element ) {
996
                // FIXME: habria que expandir los padres hasta llegar a este.
997
                this.view.treeElements.expandPath(path);
998
                return;
999
            }
1000
        }
1001
    }
1002

    
1003
    @Override
1004
    public void expandElement(Predicate<Element> condition) {
1005
        if( condition == null ) {
1006
          return;
1007
        }
1008
        this.initializeTree();
1009
        for (int i = 0; i < this.view.treeElements.getRowCount(); i++) {
1010
            TreePath path = this.view.treeElements.getPathForRow(i);
1011
            if( condition.test((Element) path.getLastPathComponent()) ) {
1012
                // FIXME: habria que expandir los padres hasta llegar a este.
1013
                this.view.treeElements.expandPath(path);
1014
                return;
1015
            }
1016
        }
1017
    }
1018

    
1019
    @Override
1020
    public Object getProperty(String name) {
1021
        return this.builderConfig.getProperty(name);
1022
    }
1023

    
1024
    @Override
1025
    public void setProperty(String name, Object value) {
1026
        this.builderConfig.setProperty(name, value);
1027
    }
1028

    
1029
    @Override
1030
    public Map<String, Object> getProperties() {
1031
        return this.builderConfig.getProperties();
1032
    }
1033

    
1034
    @Override
1035
    public boolean allowAggregates() {
1036
      return this.builderConfig.allowAggregates();
1037
    }
1038

    
1039
    @Override
1040
    public boolean allowAggregates(boolean allow) {
1041
      return this.builderConfig.allowAggregates(allow);
1042
    }
1043

    
1044
    @Override
1045
    public java.util.function.Function<String, Integer> isFunctionAllowed() {
1046
      return this.builderConfig.isFunctionAllowed();
1047
    }
1048

    
1049
    @Override
1050
    public void setAllowedFunctions(java.util.function.Function<String, Integer> allow) {
1051
      this.builderConfig.setAllowedFunctions(allow);
1052
    }
1053
    
1054
    @Override
1055
    public void addSuggestionFactory(Factory factory) {
1056
      this.builderConfig.addSuggestionFactory(factory);
1057
    }
1058

    
1059
    @Override
1060
    public String getSuggestion(Expression expression) {
1061
      return this.builderConfig.getSuggestion(expression);
1062
    }
1063

    
1064
    @Override
1065
    public void copyConfigFrom(ExpressionBuilderConfig other) {
1066
      this.builderConfig.copyConfigFrom(other);
1067
    }
1068
    
1069
    @Override
1070
    public Function<String, Integer> getAllowedFunctions() {
1071
      return this.builderConfig.getAllowedFunctions();
1072
    }
1073

    
1074
    @Override
1075
    public Collection<Factory> getSuggestionFactories() {
1076
      return this.builderConfig.getSuggestionFactories();
1077
    }
1078

    
1079
    @Override
1080
    public void removeAllElements() {
1081
      this.builderConfig.removeAllElements();
1082
    }
1083

    
1084
}