/* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 . */ package com.sun.star.script.framework.provider.beanshell; import com.sun.star.script.framework.container.ScriptMetaData; import com.sun.star.script.framework.provider.ClassLoaderFactory; import com.sun.star.script.framework.provider.ScriptEditorBase; import com.sun.star.script.framework.provider.SwingInvocation; import com.sun.star.script.provider.XScriptContext; import java.awt.BorderLayout; import java.awt.FlowLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.awt.Dimension; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.URL; import java.util.HashMap; import java.util.Map; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JFrame; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JToolBar; import javax.swing.BorderFactory; public class ScriptEditorForBeanShell extends ScriptEditorBase implements ActionListener { private JFrame frame; private String filename; private ScriptSourceModel model; private ScriptSourceView view; private URL scriptURL = null; private ClassLoader cl = null; private JButton saveBtn; // global ScriptEditorForBeanShell returned for getEditor() calls private static ScriptEditorForBeanShell theScriptEditorForBeanShell; // global list of ScriptEditors, key is [external form of URL] of file being edited private static Map BEING_EDITED = new HashMap(); // template for new BeanShell scripts private static String BSHTEMPLATE; // try to load the template for BeanShell scripts static { BSHTEMPLATE = "// BeanShell script"; try { URL url = ScriptEditorForBeanShell.class.getResource("template.bsh"); if (url != null) { InputStream in = url.openStream(); StringBuilder buf = new StringBuilder(); byte[] b = new byte[1024]; int len; while ((len = in.read(b)) != -1) { buf.append(new String(b, 0, len)); } in.close(); BSHTEMPLATE = buf.toString(); } } catch (IOException ioe) { } catch (Exception e) { } } /** * Returns the global ScriptEditorForBeanShell instance. */ public static synchronized ScriptEditorForBeanShell getEditor() { if (theScriptEditorForBeanShell == null) { theScriptEditorForBeanShell = new ScriptEditorForBeanShell(); } return theScriptEditorForBeanShell; } /** * Get the ScriptEditorForBeanShell instance for this URL * * @param url The URL of the script source file * * @return The ScriptEditorForBeanShell associated with * the given URL if one exists, otherwise null. */ public static ScriptEditorForBeanShell getEditor(URL url) { synchronized (BEING_EDITED) { return BEING_EDITED.get(url.toExternalForm()); } } /** * Returns whether or not the script source being edited in this * ScriptEditorForBeanShell has been modified */ public boolean isModified() { return view.isModified(); } /** * Returns the text being displayed in this ScriptEditorForBeanShell * * @return The text displayed in this ScriptEditorForBeanShell */ public String getText() { return view.getText(); } /** * Returns the template text for BeanShell scripts * * @return The template text for BeanShell scripts */ public String getTemplate() { return BSHTEMPLATE; } /** * Returns the default extension for BeanShell scripts * * @return The default extension for BeanShell scripts */ public String getExtension() { return "bsh"; } /** * Indicates the line where error occurred * */ public void indicateErrorLine(int lineNum) { model.indicateErrorLine(lineNum); } /** * Executes the script edited by the editor * */ public Object execute() throws Exception { if (!isMacroExecutionEnabled()) { showErrorMessage("Macro Execution has been disabled."); return null; } frame.toFront(); return model.execute(context, cl); } /** * Opens an editor window for the specified ScriptMetaData. * If an editor window is already open for that data it will be * moved to the front. * * @param context The context in which to execute the script * @param entry The metadata describing the script */ public void edit(final XScriptContext context, ScriptMetaData entry) { if (entry != null) { try { ClassLoader cl = null; try { cl = ClassLoaderFactory.getURLClassLoader(entry); } catch (Exception ignore) { // TODO re-examine error handling } final ClassLoader theCl = cl; final URL url = entry.getSourceURL(); SwingInvocation.invoke( new Runnable() { public void run() { ScriptEditorForBeanShell editor; synchronized (BEING_EDITED) { editor = BEING_EDITED.get(url.toExternalForm()); if (editor == null) { editor = new ScriptEditorForBeanShell( context, theCl, url); BEING_EDITED.put(url.toExternalForm(), editor); } } editor.frame.toFront(); } }); } catch (IOException ioe) { showErrorMessage("Error loading file: " + ioe.getMessage()); } } } private ScriptEditorForBeanShell() { } private ScriptEditorForBeanShell(XScriptContext context, ClassLoader cl, URL url) { setContext(context); this.scriptURL = url; this.model = new ScriptSourceModel(url); this.filename = ScriptMetaData.getFileName(url); this.cl = cl; try { Class c = Class.forName("org.openoffice.netbeans.editor.NetBeansSourceView"); Class[] types = new Class[] { ScriptSourceModel.class }; java.lang.reflect.Constructor ctor = c.getConstructor(types); if (ctor != null) { Object[] args = new Object[] { this.model }; this.view = (ScriptSourceView) ctor.newInstance(args); } else { this.view = new PlainSourceView(model); } } catch (java.lang.Error err) { this.view = new PlainSourceView(model); } catch (Exception e) { this.view = new PlainSourceView(model); } this.model.setView(this.view); initUI(); this.view.addListener(new UnsavedChangesListener() { @Override public void onUnsavedChanges(boolean isUnsaved) { if(filename != null) { // enable or disable save button depending on unsaved changes saveBtn.setEnabled(isUnsaved); } } }); frame.setVisible(true); } private void initUI() { frame = new JFrame("BeanShell Debug Window: " + filename); frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); frame.addWindowListener( new WindowAdapter() { @Override public void windowClosing(WindowEvent e) { doClose(); } } ); String[] labels = {"Run", "Clear", "Save","Undo","Redo"}; JToolBar toolbar = new JToolBar(); toolbar.setRollover(true); for (String label : labels) { JButton b = new JButton(label); b.setToolTipText(label); b.addActionListener(this); toolbar.add(b); toolbar.addSeparator(); // disable save button on start if (label.equals("Save")) { b.setEnabled(false); saveBtn = b; } } frame.getContentPane().add((JComponent)view, BorderLayout.CENTER); frame.add(toolbar, BorderLayout.NORTH); frame.pack(); frame.setSize(590, 480); frame.setLocation(300, 200); frame.setMinimumSize(new Dimension(500, 300)); } private void doClose() { if (view.isModified()) { int result = JOptionPane.showConfirmDialog(frame, "The script has been modified. Do you want to save the changes?"); if (result == JOptionPane.CANCEL_OPTION) { // don't close the window, just return return; } else if (result == JOptionPane.YES_OPTION) { boolean saveSuccess = saveTextArea(); if (!saveSuccess) { return; } } } frame.dispose(); shutdown(); } private boolean saveTextArea() { boolean result = true; if (!view.isModified()) { return true; } OutputStream fos = null; try { String s = view.getText(); fos = scriptURL.openConnection().getOutputStream(); if (fos != null) { fos.write(s.getBytes()); } else { showErrorMessage( "Error saving script: Could not open stream for file"); result = false; } view.setModified(false); } catch (IOException ioe) { showErrorMessage("Error saving script: " + ioe.getMessage()); result = false; } catch (Exception e) { showErrorMessage("Error saving script: " + e.getMessage()); result = false; } finally { if (fos != null) { try { fos.flush(); } catch (IOException ignore) { } try { fos.close(); } catch (IOException ignore) { } } } return result; } private void shutdown() { synchronized (BEING_EDITED) { BEING_EDITED.remove(scriptURL.toExternalForm()); } } public void actionPerformed(ActionEvent e) { String actionCommand = e.getActionCommand(); if (actionCommand.equals("Run")) { try { execute(); } catch (Exception invokeException) { showErrorMessage(invokeException.toString()); } } else if (actionCommand.equals("Save")) { saveTextArea(); } else if (actionCommand.equals("Clear")) { view.clear(); } else if(actionCommand.equals("Undo")){ view.undo(); } else if(actionCommand.equals("Redo")){ view.redo(); } } }