Java tutorial
/* * OrbisGIS is a GIS application dedicated to scientific spatial simulation. * This cross-platform GIS is developed at French IRSTV institute and is able to * manipulate and create vector and raster spatial information. * * OrbisGIS is distributed under GPL 3 license. It is produced by the "Atelier SIG" * team of the IRSTV Institute <http://www.irstv.fr/> CNRS FR 2488. * * Copyright (C) 2007-2012 IRSTV (FR CNRS 2488) * Copyright (C) 2015-2018 Lab-STICC CNRS, UMR 6285. * * This file is part of OrbisGIS. * * OrbisGIS is free software: you can redistribute it and/or modify it under the * terms of the GNU General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later * version. * * OrbisGIS is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with * OrbisGIS. If not, see <http://www.gnu.org/licenses/>. * * For more information, please consult: <http://www.orbisgis.org/> * or contact directly: * info_at_ orbisgis.org */ package org.orbisgis.groovy; import groovy.lang.GroovyShell; import groovy.sql.Sql; import org.apache.commons.io.FileUtils; import org.fife.rsta.ac.LanguageSupportFactory; import org.fife.rsta.ac.groovy.GroovyLanguageSupport; import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea; import org.fife.ui.rsyntaxtextarea.SyntaxConstants; import org.fife.ui.rtextarea.RTextScrollPane; import org.orbisgis.commons.progress.SwingWorkerPM; import org.orbisgis.coremap.layerModel.MapContext; import org.orbisgis.groovy.icons.GroovyIcon; import org.orbisgis.mapeditorapi.MapElement; import org.orbisgis.sif.CommentUtil; import org.orbisgis.sif.UIFactory; import org.orbisgis.sif.components.OpenFilePanel; import org.orbisgis.sif.components.actions.ActionCommands; import org.orbisgis.sif.components.actions.DefaultAction; import org.orbisgis.sif.components.findReplace.FindReplaceDialog; import org.orbisgis.sif.docking.DockingPanelParameters; import org.orbisgis.sif.edition.*; import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.Reference; import org.osgi.service.component.annotations.ReferenceCardinality; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.xnap.commons.i18n.I18n; import org.xnap.commons.i18n.I18nFactory; import javax.sql.DataSource; import javax.swing.*; import javax.swing.event.CaretListener; import javax.swing.event.DocumentListener; import java.awt.*; import java.awt.event.ActionListener; import java.awt.event.InputEvent; import java.awt.event.KeyEvent; import java.beans.EventHandler; import java.beans.PropertyChangeListener; import java.io.File; import java.io.IOException; import java.io.PrintStream; import java.util.*; import java.util.concurrent.ExecutorService; /** * Create the groovy console panel * * @author Erwan Bocher * @author Sylvain Palominos */ @Component(service = EditorDockable.class) public class GroovyConsolePanel extends JPanel implements EditorDockable { public static final String EDITOR_NAME = "Groovy"; private static final I18n I18N = I18nFactory.getI18n(GroovyConsolePanel.class); private static final Logger LOGGER = LoggerFactory.getLogger("gui." + GroovyConsolePanel.class); private static final Logger LOGGER_POPUP = LoggerFactory.getLogger("gui.popup" + GroovyConsolePanel.class); private final SLF4JOutputStream infoLogger = new SLF4JOutputStream(LOGGER, SLF4JOutputStream.Level.INFO); private final SLF4JOutputStream errorLogger = new SLF4JOutputStream(LOGGER, SLF4JOutputStream.Level.ERROR); private DockingPanelParameters parameters = new DockingPanelParameters(); private ActionCommands actions = new ActionCommands(); private GroovyLanguageSupport gls; private RTextScrollPane centerPanel; private RSyntaxTextArea scriptPanel; private DefaultAction executeAction; private DefaultAction executeSelectedAction; private DefaultAction clearAction; private DefaultAction saveAction; private DefaultAction saveAsAction; private DefaultAction findAction; private DefaultAction commentAction; private DefaultAction blockCommentAction; private FindReplaceDialog findReplaceDialog; private int line = 0; private int character = 0; private MapElement mapElement; private static final String MESSAGEBASE = "%d | %d"; private JLabel statusMessage = new JLabel(); private Map<String, Object> variables = new HashMap<String, Object>(); private Map<String, Object> properties = new HashMap<String, Object>(); private ExecutorService executorService; private GroovyElement groovyElement; /** * Create the groovy console panel */ public GroovyConsolePanel() { setLayout(new BorderLayout()); add(getCenterPanel(), BorderLayout.CENTER); add(statusMessage, BorderLayout.SOUTH); init(); } @Reference(cardinality = ReferenceCardinality.OPTIONAL) public void setDataSource(DataSource ds) { properties.put("sql", new Sql(ds)); } public void unsetDataSource(DataSource ds) { properties.remove("sql"); } @Reference(cardinality = ReferenceCardinality.OPTIONAL) public void setEditorManager(EditorManager editorManager) { // If a map is already loaded fetch it in the EditorManager if (editorManager != null) { try { mapElement = MapElement.fetchFirstMapElement(editorManager); } catch (Exception ex) { LOGGER.error(ex.getLocalizedMessage(), ex); } } if (mapElement != null) { setMapContext(mapElement.getMapContext()); } } @Reference public void setExecutorService(ExecutorService executorService) { this.executorService = executorService; } public void unsetExecutorService(ExecutorService executorService) { this.executorService = null; } private void execute(SwingWorker swingWorker) { if (executorService != null) { executorService.execute(swingWorker); } else { swingWorker.execute(); } } public void unsetEditorManager(EditorManager editorManager) { } /** * Init the groovy panel with all docking parameters and set the necessary * properties to the console shell */ private void init() { properties.put("out", new PrintStream(infoLogger)); properties.put("err", new PrintStream(errorLogger)); parameters.setName(EDITOR_NAME); parameters.setTitle(I18N.tr("Groovy")); parameters.setTitleIcon(new ImageIcon(GroovyConsolePanel.class.getResource("icon.png"))); parameters.setDockActions(getActions().getActions()); this.setEditableElement(new GroovyElement()); } /** * Get the action manager. * * @return ActionCommands instance. */ public ActionCommands getActions() { return actions; } /** * The main panel to write and execute a groovy script. * * @return */ private RTextScrollPane getCenterPanel() { if (centerPanel == null) { initActions(); LanguageSupportFactory lsf = LanguageSupportFactory.get(); gls = (GroovyLanguageSupport) lsf.getSupportFor(SyntaxConstants.SYNTAX_STYLE_GROOVY); scriptPanel = new RSyntaxTextArea(); scriptPanel.setLineWrap(true); lsf.register(scriptPanel); scriptPanel.setSyntaxEditingStyle(RSyntaxTextArea.SYNTAX_STYLE_GROOVY); scriptPanel .addCaretListener(EventHandler.create(CaretListener.class, this, "onScriptPanelCaretUpdate")); scriptPanel.getDocument().addDocumentListener( EventHandler.create(DocumentListener.class, this, "onUserSelectionChange")); scriptPanel.clearParsers(); scriptPanel.setTabsEmulated(true); actions.setAccelerators(scriptPanel); // Actions will be set on the scriptPanel PopupMenu scriptPanel.getPopupMenu().addSeparator(); actions.registerContainer(scriptPanel.getPopupMenu()); centerPanel = new RTextScrollPane(scriptPanel); onUserSelectionChange(); } return centerPanel; } /** * Create actions instances * * Each action is put in the Popup menu and the tool bar Their shortcuts are * registered also in the editor */ private void initActions() { //Execute action executeAction = new DefaultAction(GroovyConsoleActions.A_EXECUTE, I18N.tr("Execute"), I18N.tr("Execute the groovy script"), GroovyIcon.getIcon("execute"), EventHandler.create(ActionListener.class, this, "onExecute"), KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, InputEvent.CTRL_DOWN_MASK)).setLogicalGroup("custom"); actions.addAction(executeAction); //Execute Selected SQL executeSelectedAction = new DefaultAction(GroovyConsoleActions.A_EXECUTE_SELECTION, I18N.tr("Execute selected"), I18N.tr("Run selected code"), GroovyIcon.getIcon("execute_selection"), EventHandler.create(ActionListener.class, this, "onExecuteSelected"), KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, InputEvent.ALT_DOWN_MASK)).setLogicalGroup("custom") .setAfter(GroovyConsoleActions.A_EXECUTE); actions.addAction(executeSelectedAction); //Clear action clearAction = new DefaultAction(GroovyConsoleActions.A_CLEAR, I18N.tr("Clear"), I18N.tr("Erase the content of the editor"), GroovyIcon.getIcon("erase"), EventHandler.create(ActionListener.class, this, "onClear"), null); actions.addAction(clearAction); //Open action actions.addAction(new DefaultAction(GroovyConsoleActions.A_OPEN, I18N.tr("Open"), I18N.tr("Load a file in this editor"), GroovyIcon.getIcon("open"), EventHandler.create(ActionListener.class, this, "onOpenFile"), KeyStroke.getKeyStroke(KeyEvent.VK_O, InputEvent.CTRL_DOWN_MASK))); //Save saveAction = new DefaultAction(GroovyConsoleActions.A_SAVE, I18N.tr("Save"), I18N.tr("Save the editor content into a file"), GroovyIcon.getIcon("save"), EventHandler.create(ActionListener.class, this, "onSaveFile"), KeyStroke.getKeyStroke(KeyEvent.VK_S, InputEvent.CTRL_DOWN_MASK)); actions.addAction(saveAction); // Save As saveAsAction = new DefaultAction(GroovyConsoleActions.A_SAVE, I18N.tr("Save As"), I18N.tr("Save the editor content into a new file"), GroovyIcon.getIcon("page_white_save"), EventHandler.create(ActionListener.class, this, "onSaveAsNewFile"), KeyStroke.getKeyStroke("ctrl maj s")); actions.addAction(saveAsAction); //Find action findAction = new DefaultAction(GroovyConsoleActions.A_SEARCH, I18N.tr("Search.."), I18N.tr("Search text in the document"), GroovyIcon.getIcon("find"), EventHandler.create(ActionListener.class, this, "openFindReplaceDialog"), KeyStroke.getKeyStroke(KeyEvent.VK_F, InputEvent.CTRL_DOWN_MASK)) .addStroke(KeyStroke.getKeyStroke(KeyEvent.VK_H, InputEvent.CTRL_DOWN_MASK)); actions.addAction(findAction); // Comment/Uncomment commentAction = new DefaultAction(GroovyConsoleActions.A_COMMENT, I18N.tr("(Un)comment"), I18N.tr("(Un)comment the selected text"), null, EventHandler.create(ActionListener.class, this, "onComment"), KeyStroke.getKeyStroke("alt C")) .setLogicalGroup("format"); actions.addAction(commentAction); // Block Comment/Uncomment blockCommentAction = new DefaultAction(GroovyConsoleActions.A_BLOCKCOMMENT, I18N.tr("Block (un)comment"), I18N.tr("Block (un)comment the selected text."), null, EventHandler.create(ActionListener.class, this, "onBlockComment"), KeyStroke.getKeyStroke("alt shift C")).setLogicalGroup("format"); actions.addAction(blockCommentAction); } /** * Clear the content of the console */ public void onClear() { if (scriptPanel.getDocument().getLength() != 0) { int answer = JOptionPane.showConfirmDialog(this, I18N.tr("Do you want to clear the contents of the console?"), I18N.tr("Clear script"), JOptionPane.YES_NO_OPTION); if (answer == JOptionPane.YES_OPTION) { scriptPanel.setText(""); } } } /** * Open a dialog that let the user to select a file and save the content * of the Groovy editor into this file. */ public void onSaveAsNewFile() { File oldDocPath = groovyElement.getDocumentPath(); groovyElement.setDocumentPath(null); onSaveFile(); if (groovyElement.getDocumentPath() == null && oldDocPath != null) { // If canceled groovyElement.setDocumentPath(oldDocPath); } } /** * Open a dialog that let the user select a file and save the content of the * Groovy editor into this file. */ public void onSaveFile() { try { groovyElement.save(); } catch (EditableElementException ex) { LOGGER.error(ex.getLocalizedMessage(), ex); } if (!groovyElement.isModified()) { LOGGER.info(I18N.tr("The file has been saved.")); } } /** * Open a dialog that let the user select a file and add or replace the * content of the sql editor. */ public void onOpenFile() { final OpenFilePanel inFilePanel = new OpenFilePanel("groovyConsoleInFile", I18N.tr("Open script")); inFilePanel.addFilter("groovy", I18N.tr("Groovy Script (*.groovy)")); inFilePanel.loadState(); if (UIFactory.showDialog(inFilePanel)) { int answer = JOptionPane.NO_OPTION; if (scriptPanel.getDocument().getLength() > 0) { answer = JOptionPane.showConfirmDialog(this, I18N.tr("Do you want to clear all before loading the file ?"), I18N.tr("Open file"), JOptionPane.YES_NO_CANCEL_OPTION); } String text; try { text = FileUtils.readFileToString(inFilePanel.getSelectedFile()); } catch (IOException e1) { LOGGER.error(I18N.tr("Cannot write the script."), e1); return; } if (answer == JOptionPane.YES_OPTION) { scriptPanel.setText(text); } else if (answer == JOptionPane.NO_OPTION) { scriptPanel.append(text); } } } /** * Update the row:column label */ public void onScriptPanelCaretUpdate() { line = scriptPanel.getCaretLineNumber() + 1; character = scriptPanel.getCaretOffsetFromLineStart(); statusMessage.setText(String.format(MESSAGEBASE, line, character)); } /** * Change the status of the button when the console is empty or not. */ public void onUserSelectionChange() { String text = scriptPanel.getText().trim(); if (text.isEmpty()) { executeAction.setEnabled(false); executeSelectedAction.setEnabled(false); clearAction.setEnabled(false); saveAction.setEnabled(false); findAction.setEnabled(false); commentAction.setEnabled(false); blockCommentAction.setEnabled(false); } else { executeAction.setEnabled(true); executeSelectedAction.setEnabled(true); clearAction.setEnabled(true); saveAction.setEnabled(true); findAction.setEnabled(true); commentAction.setEnabled(true); blockCommentAction.setEnabled(true); } } /** * User click on execute script button */ public void onExecute() { if (executeAction.isEnabled()) { String text = scriptPanel.getText().trim(); GroovyJob groovyJob = new GroovyJob(text, properties, variables, new SLF4JOutputStream[] { infoLogger, errorLogger }, executeAction); execute(groovyJob); } } /** * Execute the selected groovy code */ public void onExecuteSelected() { if (executeSelectedAction.isEnabled()) { String selected = scriptPanel.getSelectedText(); if (selected != null) { GroovyJob groovyJob = new GroovyJob(selected.trim(), properties, variables, new SLF4JOutputStream[] { infoLogger, errorLogger }, executeAction); execute(groovyJob); } } } /** * Expose the map context in the groovy interpreter * * @param mc MapContext instance */ private void setMapContext(MapContext mc) { try { if (mc != null) { variables.put("mc", mc); } else { variables.remove("mc"); } } catch (Error ex) { LOGGER.error(ex.getLocalizedMessage(), ex); } } @Override public boolean match(EditableElement editableElement) { return editableElement instanceof MapElement; } @Override public EditableElement getEditableElement() { return groovyElement; } @Override public void setEditableElement(EditableElement editableElement) { if (editableElement instanceof GroovyElement) { this.groovyElement = (GroovyElement) editableElement; groovyElement.setDocument(scriptPanel); groovyElement.addPropertyChangeListener(GroovyElement.PROP_DOCUMENT_PATH, EventHandler.create(PropertyChangeListener.class, this, "onPathChanged")); onPathChanged(); LoadScript loadScript = new LoadScript(groovyElement); if (executorService != null) { executorService.execute(loadScript); } else { loadScript.execute(); } } } public void onPathChanged() { if (!groovyElement.getDocumentPathString().isEmpty()) { getDockingParameters().setTitle(groovyElement.getDocumentPath().getName()); } } /** * Dispose */ public void freeResources() { if (gls != null) { gls.uninstall(scriptPanel); } } @Override public DockingPanelParameters getDockingParameters() { return parameters; } @Override public JComponent getComponent() { return this; } /** * (Un)comment the selected text. */ public void onComment() { CommentUtil.commentOrUncommentJava(scriptPanel); } /** * Block (un)comment the selected text. */ public void onBlockComment() { CommentUtil.blockCommentOrUncomment(scriptPanel); } /** * Open one instanceof the find replace dialog */ public void openFindReplaceDialog() { if (findReplaceDialog == null) { findReplaceDialog = new FindReplaceDialog(scriptPanel, UIFactory.getMainFrame()); } findReplaceDialog.setAlwaysOnTop(true); findReplaceDialog.setVisible(true); } /** * Execute the provided script in groovy */ public static class GroovyJob extends SwingWorkerPM { private String script; private SLF4JOutputStream[] loggers; private Map<String, Object> variables; private Map<String, Object> properties; private Action executeAction; private long startScript; private Thread thread; private CancelClosure closure; public GroovyJob(String script, Map<String, Object> properties, Map<String, Object> variables, SLF4JOutputStream[] loggers, Action executeAction) { this.script = script; this.loggers = loggers; this.variables = variables; this.properties = properties; this.executeAction = executeAction; this.closure = new CancelClosure(this); //Try to set the cancel closure with the groovy Sql object if found. Sql sql = (Sql) properties.get("sql"); if (sql != null) { sql.withStatement(closure); } setTaskName(I18N.tr("Execute Groovy script")); } @Override protected Object doInBackground() throws Exception { variables.put("pm", getProgressMonitor()); executeAction.setEnabled(false); try { final GroovyShell groovyShell = new GroovyShell(); for (Map.Entry<String, Object> variable : variables.entrySet()) { groovyShell.setVariable(variable.getKey(), variable.getValue()); } for (Map.Entry<String, Object> property : properties.entrySet()) { groovyShell.setProperty(property.getKey(), property.getValue()); } startScript = System.currentTimeMillis(); Runnable scriptRun = new Runnable() { @Override public void run() { try { groovyShell.evaluate(script); } catch (Exception e) { LOGGER.error(I18N.tr("Cannot execute this R script.\nCause : " + e.getMessage())); } finally { executeAction.setEnabled(true); } } }; thread = new Thread(scriptRun); thread.start(); thread.join(); } catch (Exception e) { LOGGER.error(I18N.tr("Cannot execute the Groovy script") + "\n" + e.getLocalizedMessage()); } finally { executeAction.setEnabled(true); for (SLF4JOutputStream logger : loggers) { try { logger.flush(); } catch (IOException e) { LOGGER.error(I18N.tr("Cannot display the output of the console"), e); } } } return null; } @Override protected void done() { LOGGER.info(I18N.tr("Groovy script executed in {0} seconds", (System.currentTimeMillis() - startScript) / 1000.)); } @Override public void cancel() { super.cancel(); //Try to cancel a running sql statement if (closure != null) { closure.cancel(); } thread.interrupt(); try { thread.join(500); } catch (InterruptedException ignored) { } if (thread.isAlive()) { LOGGER.info(I18N.tr("Forcing script to cancel.")); //TODO find a good way to kill the script thread thread.stop(); } LOGGER.info(I18N.tr("Script canceled.")); executeAction.setEnabled(true); } } }