/******************************************************************************* * Copyright 2010 Simon Mieth * * Licensed 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 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package org.kabeja.processing.scripting.impl; import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.FlowLayout; import java.awt.event.ActionEvent; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.PrintStream; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import javax.swing.AbstractAction; import javax.swing.Action; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JFileChooser; import javax.swing.JFrame; import javax.swing.JMenu; import javax.swing.JMenuBar; import javax.swing.JMenuItem; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JSeparator; import javax.swing.JTextArea; import javax.swing.JToolBar; import javax.swing.text.BadLocationException; import org.kabeja.DraftDocument; import org.kabeja.processing.AbstractPostProcessor; import org.kabeja.processing.ProcessorException; import org.kabeja.ui.DraftDocumentViewComponent; import org.kabeja.ui.UIException; import org.kabeja.ui.event.DraftDocumentChangeEventProvider; import org.kabeja.ui.event.DraftDocumentChangeListener; import org.mozilla.javascript.Context; import org.mozilla.javascript.ContextFactory; import org.mozilla.javascript.ErrorReporter; import org.mozilla.javascript.EvaluatorException; import org.mozilla.javascript.Scriptable; import org.mozilla.javascript.ScriptableObject; import de.miethxml.toolkit.ui.UIUtils; public class JavaScriptShell extends AbstractPostProcessor implements DraftDocumentViewComponent, DraftDocumentChangeEventProvider { protected final static String COMMAND_PREFIX = "js>"; protected JFrame frame; protected JTextArea textArea; protected HashMap actions = new HashMap(); protected ArrayList history = new ArrayList(); protected int historyPos = 0; protected ScriptWorker worker; protected String title = "JSShell"; protected ArrayList listeners = new ArrayList(); protected DraftDocument doc; public void process(DraftDocument doc, Map context) throws ProcessorException { worker = new ScriptWorker(doc); worker.start(); this.init(); // wait for finished shell editing while (worker.isAlive()) { try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } } public void setProperties(Map properties) { } protected void init() { frame = new JFrame(getTitle()); frame.setJMenuBar(this.createMenuBar()); frame.getContentPane().setLayout(new BorderLayout()); frame.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { // capture the second close-event and ignore dispose(); } }); frame.getContentPane().add(getView(), BorderLayout.CENTER); JPanel p = new JPanel(new FlowLayout(FlowLayout.RIGHT, 2, 2)); JButton button = new JButton((Action) actions.get("close")); p.add(button); frame.getContentPane().add(p, BorderLayout.SOUTH); frame.setSize(new Dimension(640, 480)); frame.setVisible(true); newShellLine(); } protected JToolBar createToolbar() { JToolBar toolbar = new JToolBar(); JButton button = new JButton((Action) actions.get("copy")); button.setToolTipText(button.getText()); button.setText(""); toolbar.add(button); button = new JButton((Action) actions.get("paste")); button.setToolTipText(button.getText()); button.setText(""); toolbar.add(button); button = new JButton((Action) actions.get("cut")); button.setToolTipText(button.getText()); button.setText(""); toolbar.add(button); button = new JButton((Action) actions.get("reload")); button.setToolTipText(button.getText()); button.setText(""); toolbar.add(button); return toolbar; } protected JMenuBar createMenuBar() { JMenuBar menubar = new JMenuBar(); JMenu menu = new JMenu("File"); JMenuItem item = new JMenuItem((Action) actions.get("open")); item.setToolTipText(item.getText()); menu.add(item); item = new JMenuItem((Action) actions.get("save")); menu.add(item); menu.add(new JSeparator()); item = new JMenuItem((Action) actions.get("close")); menu.add(item); menubar.add(menu); // menu = new JMenu("Edit"); // item = new JMenuItem((Action) actions.get("copy")); // menu.add(item); // item = new JMenuItem((Action) actions.get("paste")); // menu.add(item); // item = new JMenuItem((Action) actions.get("cut")); // menu.add(item); // menubar.add(menu); return menubar; } protected void initActions() { Action action = new AbstractAction("Cut", new ImageIcon(UIUtils.resourceToBytes(this.getClass(), "/icons/cut_edit.gif"))) { public void actionPerformed(ActionEvent e) { textArea.cut(); } }; actions.put("cut", action); action = new AbstractAction("Paste", new ImageIcon(UIUtils.resourceToBytes(this.getClass(), "/icons/paste_edit.gif"))) { public void actionPerformed(ActionEvent e) { textArea.paste(); } }; actions.put("paste", action); action = new AbstractAction("Copy", new ImageIcon(UIUtils.resourceToBytes(this.getClass(), "/icons/copy_edit.gif"))) { public void actionPerformed(ActionEvent e) { textArea.copy(); } }; actions.put("copy", action); action = new AbstractAction("Close") { public void actionPerformed(ActionEvent e) { dispose(); } }; actions.put("close", action); action = new AbstractAction("Save") { public void actionPerformed(ActionEvent e) { JFileChooser fs = new JFileChooser(); int r = fs.showSaveDialog(frame); if (r == JFileChooser.APPROVE_OPTION) { try { BufferedWriter out = new BufferedWriter(new OutputStreamWriter( new FileOutputStream( fs.getSelectedFile()))); Iterator i = history.iterator(); while (i.hasNext()) { out.write((String) i.next()); out.newLine(); } out.flush(); out.close(); } catch (IOException e1) { e1.printStackTrace(); } } } }; actions.put("save", action); action = new AbstractAction("Open") { public void actionPerformed(ActionEvent e) { textArea.setText(COMMAND_PREFIX); history.clear(); JFileChooser fs = new JFileChooser(); int r = fs.showOpenDialog(frame); if (r == JFileChooser.APPROVE_OPTION) { try { BufferedReader in = new BufferedReader(new InputStreamReader( new FileInputStream( fs.getSelectedFile()))); String line; while ((line = in.readLine()) != null) { textArea.append(line); evalString(COMMAND_PREFIX + line); } in.close(); } catch (IOException e1) { e1.printStackTrace(); } } } }; actions.put("open", action); action = new AbstractAction("Update views", new ImageIcon(UIUtils.resourceToBytes(this.getClass(), "/icons/reload.gif"))) { public void actionPerformed(ActionEvent e) { fireDXFDocumentChangeEvent(); } }; actions.put("reload", action); } protected String getLineAtCaretPosition() { try { int lineNumber = this.textArea.getLineOfOffset(this.textArea.getCaretPosition()); int startOffset = this.textArea.getLineStartOffset(lineNumber); int length = this.textArea.getCaretPosition() - startOffset; String line = this.textArea.getText(startOffset, length); return line; } catch (BadLocationException e) { // TODO Auto-generated catch block e.printStackTrace(); } return ""; } protected int getStartOffsetAtCaretPosition() { try { int lineNumber = textArea.getLineOfOffset(textArea.getCaretPosition()); int startOffset = textArea.getLineStartOffset(lineNumber); return startOffset; } catch (BadLocationException e) { e.printStackTrace(); } return 0; } protected void evalString(String line) { textArea.append("\n"); if (line.startsWith(COMMAND_PREFIX)) { String script = line.substring(COMMAND_PREFIX.length()); history.add(script); worker.executeScript(script); } else { newShellLine(); } } protected void newShellLine() { textArea.append(COMMAND_PREFIX); textArea.setCaretPosition(textArea.getDocument().getLength()); } protected void write(String str) { try { this.textArea.getDocument() .insertString(this.textArea.getCaretPosition(), str, null); } catch (BadLocationException e) { e.printStackTrace(); } } protected void dispose() { frame.setVisible(false); frame.dispose(); worker.dispose(); } public void showDraftDocument(DraftDocument doc) throws UIException { this.doc = doc; worker = new ScriptWorker(doc); worker.start(); } public String getTitle() { return this.title; } public JComponent getView() { textArea = new JTextArea(); textArea.addKeyListener(new CommandKeyListener()); this.initActions(); JPanel panel = new JPanel(new BorderLayout()); panel.add(new JScrollPane(textArea), BorderLayout.CENTER); panel.add(createToolbar(), BorderLayout.NORTH); panel.setPreferredSize(new Dimension(640, 480)); newShellLine(); worker = new ScriptWorker(null); worker.start(); return panel; } public void addDraftDocumentChangeListener(DraftDocumentChangeListener listener) { this.listeners.add(listener); } public void removeDraftDocumentChangeListener( DraftDocumentChangeListener listener) { this.listeners.remove(listener); } protected void fireDXFDocumentChangeEvent() { if (this.doc != null) { Iterator i = ((ArrayList) this.listeners.clone()).iterator(); while (i.hasNext()) { DraftDocumentChangeListener l = (DraftDocumentChangeListener) i.next(); l.changed(this.doc); } } } public class CommandKeyListener extends KeyAdapter { public void keyPressed(KeyEvent e) { if (e.getKeyCode() == KeyEvent.VK_ENTER) { evalString(getLineAtCaretPosition()); e.consume(); historyPos = history.size() - 1; } else if ((e.getKeyCode() == KeyEvent.VK_BACK_SPACE) || (e.getKeyCode() == KeyEvent.VK_LEFT)) { try { int lineNumber = textArea.getLineOfOffset(textArea.getCaretPosition()); int startOffset = textArea.getLineStartOffset(lineNumber); int l = textArea.getCaretPosition() - startOffset; if (l <= COMMAND_PREFIX.length()) { e.consume(); } } catch (BadLocationException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } } else if (e.getKeyCode() == KeyEvent.VK_UP) { if ((historyPos >= 0) && (history.size() > 0)) { if ((historyPos >= history.size()) && (history.size() > 1)) { historyPos = history.size() - 2; } int start = getStartOffsetAtCaretPosition() + COMMAND_PREFIX.length(); textArea.replaceRange((String) history.get(historyPos), start, textArea.getDocument().getLength()); historyPos--; } e.consume(); } else if (e.getKeyCode() == KeyEvent.VK_DOWN) { if ((historyPos < history.size()) && (history.size() > 0)) { if ((historyPos < 0) && (history.size() > 1)) { historyPos = 1; } int start = getStartOffsetAtCaretPosition() + COMMAND_PREFIX.length(); textArea.replaceRange((String) history.get(historyPos), start, textArea.getDocument().getLength()); historyPos++; } e.consume(); } } } protected class ScriptWorker extends Thread { protected Context ctx; protected Scriptable scope; protected boolean execute = false; protected boolean dispose = false; protected String script; protected DraftDocument doc; PrintStream err; PrintStream out; protected PrintStream output = new PrintStream(new JTextAreaPrintWriter()); public ScriptWorker(DraftDocument doc) { this.doc = doc; } protected void init() { // this.ctx = ContextFactory.getGlobal().enter(); ContextFactory f = new ContextFactory(); this.ctx = f.enter(); err = System.err; out = System.out; // System.setOut(output); // System.setErr(output); this.scope = ctx.initStandardObjects(null, false); try { ScriptableObject.defineClass(this.scope, Global.class); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } this.scope = new Global(); Global.setOutput(output); ctx.setErrorReporter(new ErrorReporter() { public void error(String arg0, String arg1, int arg2, String arg3, int arg4) { textArea.append(arg0 + " from line:" + arg3 + "\n"); } public EvaluatorException runtimeError(String arg0, String arg1, int arg2, String arg3, int arg4) { textArea.append(arg0 + " from line:" + arg3 + "\n"); return new EvaluatorException(arg0); } public void warning(String arg0, String arg1, int arg2, String arg3, int arg4) { textArea.append(arg0 + " from line:" + arg3 + "\n"); } }); Object jsOut = Context.javaToJS(doc, scope); ScriptableObject.putProperty(scope, "dxf", jsOut); textArea.setText(""); if(doc!=null){ textArea.append("DXFDocument available as 'dxf'\n"); } newShellLine(); } public void run() { init(); while (!dispose) { if (execute) { try { Object result = this.ctx.evaluateString(scope, script, "<cmd>", 1, null); String r = Context.toString(result); if (!r.equals("undefined")) { synchronized (textArea) { textArea.append(r); textArea.append("\n"); } } } catch (Exception e) { e.printStackTrace(output); init(); } execute = false; newShellLine(); } else { try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } } Context.exit(); } public void executeScript(String str) { this.script = str; this.execute = true; while (execute) { try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } } } public void dispose() { System.setErr(err); System.setOut(out); this.dispose = true; } } protected class JTextAreaPrintWriter extends OutputStream { StringBuffer buf = new StringBuffer(); public void write(int b) throws IOException { if (b == '\r') { return; } else if (b == '\n') { synchronized (textArea) { textArea.append(buf.toString() + "\n"); } buf.setLength(0); } else { buf.append((char) b); } } } }