/* * Copyright (c) 2006, 2013, Oracle and/or its affiliates. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of Oracle nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* * This source code is provided to illustrate the usage of a given feature * or technique and has been deliberately simplified. Additional steps * required for a production-quality application, such as security checks, * input validation and proper error handling, might not be present in * this sample code. */ /* * This script creates a simple Notepad-like interface, which * serves as a simple script editor, runner. * * File dependency: * * gui.js -> for basic GUI functions */ /* * globalThis is used for actionHelpGlobals() and showFrame(). */ var globalThis = this; /* * JavaImporter helps in avoiding pollution of JavaScript * global namespace. We can import multiple Java packages * with this and use the JavaImporter object with "with" * statement. */ var guiPkgs = new JavaImporter(java.awt, java.awt.event, javax.swing, javax.swing.undo, javax.swing.event, javax.swing.text); // main entry point of the scriptpad application var main = function() { function createEditor() { var c = new guiPkgs.JTextArea(); c.setDragEnabled(true); c.setFont(new guiPkgs.Font("monospaced", guiPkgs.Font.PLAIN, 12)); return c; } /*const*/ var titleSuffix = "- Scriptpad"; /*const*/ var defaultTitle = "Untitled" + titleSuffix; // Scriptpad's main frame var frame; // Scriptpad's main editor var editor; // To track the current file name var curFileName = null; // To track whether the current document // has been modified or not var docChanged = false; // check and alert user for unsaved // but modified document function checkDocChanged() { if (docChanged) { // ignore zero-content untitled document if (curFileName == null && editor.document.length == 0) { return; } if (confirm("Do you want to save the changes?", "The document has changed")) { actionSave(); } } } // set a document listener to track // whether that is modified or not function setDocListener() { var doc = editor.getDocument(); docChanged = false; doc.addDocumentListener( new guiPkgs.DocumentListener() { equals: function(o) { return this === o; }, toString: function() { return "doc listener"; }, changeUpdate: function() { docChanged = true; }, insertUpdate: function() { docChanged = true; }, removeUpdate: function() { docChanged = true; } }); } // menu action functions // "File" menu // create a "new" document function actionNew() { checkDocChanged(); curFileName = null; editor.setDocument(new guiPkgs.PlainDocument()); setDocListener(); frame.setTitle(defaultTitle); editor.revalidate(); } // open an existing file function actionOpen() { checkDocChanged(); var f = fileDialog(); if (f == null) { return; } if (f.isFile() && f.canRead()) { frame.setTitle(f.getName() + titleSuffix); editor.setDocument(new guiPkgs.PlainDocument()); var progress = new guiPkgs.JProgressBar(); progress.setMinimum(0); progress.setMaximum(f.length()); var doc = editor.getDocument(); var inp = new java.io.FileReader(f); var buff = java.lang.reflect.Array.newInstance( java.lang.Character.TYPE, 4096); var nch; while ((nch = inp.read(buff, 0, buff.length)) != -1) { doc.insertString(doc.getLength(), new java.lang.String(buff, 0, nch), null); progress.setValue(progress.getValue() + nch); } inp.close(); curFileName = f.getAbsolutePath(); setDocListener(); } else { error("Can not open file: " + f, "Error opening file: " + f); } } // open script from a URL function actionOpenURL() { checkDocChanged(); var url = prompt("Address:"); if (url == null) { return; } try { var u = new java.net.URL(url); editor.setDocument(new guiPkgs.PlainDocument()); frame.setTitle(url + titleSuffix); var progress = new guiPkgs.JProgressBar(); progress.setMinimum(0); progress.setIndeterminate(true); var doc = editor.getDocument(); var inp = new java.io.InputStreamReader(u.openStream()); var buff = java.lang.reflect.Array.newInstance( java.lang.Character.TYPE, 4096); var nch; while ((nch = inp.read(buff, 0, buff.length)) != -1) { doc.insertString(doc.getLength(), new java.lang.String(buff, 0, nch), null); progress.setValue(progress.getValue() + nch); } curFileName = null; setDocListener(); } catch (e) { error("Error opening URL: " + e, "Can not open URL: " + url); } } // factored out "save" function used by // save, save as menu actions function save(file) { var doc = editor.getDocument(); frame.setTitle(file.getName() + titleSuffix); curFileName = file; var progress = new guiPkgs.JProgressBar(); progress.setMinimum(0); progress.setMaximum(file.length()); var out = new java.io.FileWriter(file); var text = new guiPkgs.Segment(); text.setPartialReturn(true); var charsLeft = doc.getLength(); var offset = 0; var min; while (charsLeft > 0) { doc.getText(offset, Math.min(4096, charsLeft), text); out.write(text.array, text.offset, text.count); charsLeft -= text.count; offset += text.count; progress.setValue(offset); java.lang.Thread.sleep(10); } out.flush(); out.close(); docChanged = false; } // file-save as menu function actionSaveAs() { var ret = fileDialog(null, true); if (ret == null) { return; } save(ret); } // file-save menu function actionSave() { if (curFileName) { save(new java.io.File(curFileName)); } else { actionSaveAs(); } } // exit from scriptpad function actionExit() { checkDocChanged(); java.lang.System.exit(0); } // "Edit" menu // cut the currently selected text function actionCut() { editor.cut(); } // copy the currently selected text to clipboard function actionCopy() { editor.copy(); } // paste clipboard content to document function actionPaste() { editor.paste(); } // select all the text in editor function actionSelectAll() { editor.selectAll(); } // "Tools" menu // run the current document as JavaScript function actionRun() { var doc = editor.getDocument(); var script = doc.getText(0, doc.getLength()); var oldFile = engine.get(javax.script.ScriptEngine.FILENAME); try { if (engine == undefined) { var m = new javax.script.ScriptEngineManager(); engine = m.getEngineByName("nashorn"); } engine.put(javax.script.ScriptEngine.FILENAME, frame.title); engine.eval(script, context); } catch (e) { error(e, "Script Error"); e.printStackTrace(); } finally { engine.put(javax.script.ScriptEngine.FILENAME, oldFile); } } // "Examples" menu // show given script as new document function showScript(title, str) { actionNew(); frame.setTitle("Example - " + title + titleSuffix); var doc = editor.document; doc.insertString(0, str, null); } // "hello world" function actionHello() { showScript(actionEval.title, "alert('Hello, world');"); } actionHello.title = "Hello, World"; // eval the "hello world"! function actionEval() { showScript(actionEval.title, "eval(\"alert('Hello, world')\");"); } actionEval.title = "Eval"; // show how to access Java static methods function actionJavaStatic() { showScript(arguments.callee.title, "// Just use Java syntax\n" + "var props = java.lang.System.getProperties();\n" + "alert(props.get('os.name'));"); } actionJavaStatic.title = "Java Static Calls"; // show how to access Java classes, methods function actionJavaAccess() { showScript(arguments.callee.title, "// just use new JavaClass();\n" + "var fr = new javax.swing.JFrame();\n" + "// call all public methods as in Java\n" + "fr.setTitle('hello');\n" + "fr.setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);\n" + "fr.setSize(200, 200);\n" + "fr.setVisible(true);"); } actionJavaAccess.title = "Java Object Access"; // show how to use Java bean conventions function actionJavaBean() { showScript(arguments.callee.title, "var fr = new javax.swing.JFrame();\n" + "fr.setSize(200, 200);\n" + "// access public get/set methods as fields\n" + "fr.defaultCloseOperation = javax.swing.WindowConstants.DISPOSE_ON_CLOSE;\n" + "fr.title = 'hello';\n" + "fr.visible = true;"); } actionJavaBean.title = "Java Beans"; // show how to implement Java interface function actionJavaInterface() { showScript(arguments.callee.title, "// use Java anonymizer class-like syntax!\n" + "var r = new java.lang.Runnable() {\n" + " run: function() {\n" + " alert('hello');\n" + " }\n" + " };\n" + "// use the above Runnable to create a Thread\n" + "new java.lang.Thread(r).start();\n" + "// For simple one method interfaces, just pass script function\n" + "new java.lang.Thread(function() { alert('world'); }).start();"); } actionJavaInterface.title = "Java Interfaces"; // show how to import Java classes, packages function actionJavaImport() { showScript(arguments.callee.title, "// use Java-like import *...\n" + "// importPackage(java.io);\n" + "// or import a specific class\n" + "// importClass(java.io.File);\n" + "// or better - import just within a scope!\n" + "var ioPkgs = JavaImporter(java.io);\n" + "with (ioPkgs) { alert(new File('.').absolutePath); }"); } actionJavaImport.title = "Java Import"; // "Help" menu /* * Shows a one liner help message for each * global function. Note that this function * depends on docString meta-data for each * function. */ function actionHelpGlobals() { var names = new java.util.ArrayList(); for (var i in globalThis) { var func = globalThis[i]; if (typeof(func) == "function" && ("docString" in func)) { names.add(i); } } java.util.Collections.sort(names); var helpDoc = new java.lang.StringBuffer(); helpDoc.append("<table border='1'>"); var itr = names.iterator(); while (itr.hasNext()) { var name = itr.next(); helpDoc.append("<tr><td>"); helpDoc.append(name); helpDoc.append("</td><td>"); helpDoc.append(globalThis[name].docString); helpDoc.append("</td></tr>"); } helpDoc.append("</table>"); var helpEditor = new guiPkgs.JEditorPane(); helpEditor.setContentType("text/html"); helpEditor.setEditable(false); helpEditor.setText(helpDoc.toString()); var scroller = new guiPkgs.JScrollPane(); var port = scroller.getViewport(); port.add(helpEditor); var helpFrame = new guiPkgs.JFrame("Help - Global Functions"); helpFrame.getContentPane().add("Center", scroller); helpFrame.setDefaultCloseOperation(guiPkgs.WindowConstants.DISPOSE_ON_CLOSE); helpFrame.pack(); helpFrame.setSize(500, 600); helpFrame.setVisible(true); } // show a simple about message for scriptpad function actionAbout() { alert("Scriptpad\nVersion 1.1", "Scriptpad"); } /* * This data is used to construct menu bar. * This way adding a menu is easier. Just add * top level menu or add an item to an existing * menu. "action" should be a function that is * called back on clicking the correponding menu. */ var menuData = [ { menu: "File", items: [ { name: "New", action: actionNew , accel: guiPkgs.KeyEvent.VK_N }, { name: "Open...", action: actionOpen, accel: guiPkgs.KeyEvent.VK_O }, { name: "Open URL...", action: actionOpenURL, accel: guiPkgs.KeyEvent.VK_U }, { name: "Save", action: actionSave, accel: guiPkgs.KeyEvent.VK_S }, { name: "Save As...", action: actionSaveAs }, { name: "-" }, { name: "Exit", action: actionExit, accel: guiPkgs.KeyEvent.VK_Q } ] }, { menu: "Edit", items: [ { name: "Cut", action: actionCut, accel: guiPkgs.KeyEvent.VK_X }, { name: "Copy", action: actionCopy, accel: guiPkgs.KeyEvent.VK_C }, { name: "Paste", action: actionPaste, accel: guiPkgs.KeyEvent.VK_V }, { name: "-" }, { name: "Select All", action: actionSelectAll, accel: guiPkgs.KeyEvent.VK_A } ] }, { menu: "Tools", items: [ { name: "Run", action: actionRun, accel: guiPkgs.KeyEvent.VK_R } ] }, { menu: "Examples", items: [ { name: actionHello.title, action: actionHello }, { name: actionEval.title, action: actionEval }, { name: actionJavaStatic.title, action: actionJavaStatic }, { name: actionJavaAccess.title, action: actionJavaAccess }, { name: actionJavaBean.title, action: actionJavaBean }, { name: actionJavaInterface.title, action: actionJavaInterface }, { name: actionJavaImport.title, action: actionJavaImport } ] }, { menu: "Help", items: [ { name: "Global Functions", action: actionHelpGlobals }, { name: "-" }, { name: "About Scriptpad", action: actionAbout } ] } ]; function setMenuAccelerator(mi, accel) { var keyStroke = guiPkgs.KeyStroke.getKeyStroke(accel, guiPkgs.Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(), false); mi.setAccelerator(keyStroke); } // create a menubar using the above menu data function createMenubar() { var mb = new guiPkgs.JMenuBar(); for (var m in menuData) { var items = menuData[m].items; var menu = new guiPkgs.JMenu(menuData[m].menu); for (var i in items) { if (items[i].name.equals("-")) { menu.addSeparator(); } else { var mi = new guiPkgs.JMenuItem(items[i].name); var action = items[i].action; mi.addActionListener(action); var accel = items[i].accel; if (accel) { setMenuAccelerator(mi, accel); } menu.add(mi); } } mb.add(menu); } return mb; } // function to add a new menu item under "Tools" menu function addTool(menuItem, action, accel) { if (typeof(action) != "function") { return; } var toolsIndex = -1; // find the index of the "Tools" menu for (var i in menuData) { if (menuData[i].menu.equals("Tools")) { toolsIndex = i; break; } } if (toolsIndex == -1) { return; } var toolsMenu = frame.getJMenuBar().getMenu(toolsIndex); var mi = new guiPkgs.JMenuItem(menuItem); mi.addActionListener(action); if (accel) { setMenuAccelerator(mi, accel); } toolsMenu.add(mi); } // create Scriptpad frame function createFrame() { frame = new guiPkgs.JFrame(); frame.setTitle(defaultTitle); frame.setBackground(guiPkgs.Color.lightGray); frame.getContentPane().setLayout(new guiPkgs.BorderLayout()); // create notepad panel var notepad = new guiPkgs.JPanel(); notepad.setBorder(guiPkgs.BorderFactory.createEtchedBorder()); notepad.setLayout(new guiPkgs.BorderLayout()); // create editor editor = createEditor(); var scroller = new guiPkgs.JScrollPane(); var port = scroller.getViewport(); port.add(editor); // add editor to notepad panel var panel = new guiPkgs.JPanel(); panel.setLayout(new guiPkgs.BorderLayout()); panel.add("Center", scroller); notepad.add("Center", panel); // add notepad panel to frame frame.getContentPane().add("Center", notepad); // set menu bar to frame and show the frame frame.setJMenuBar(createMenubar()); frame.setDefaultCloseOperation(guiPkgs.JFrame.EXIT_ON_CLOSE); frame.pack(); frame.setSize(500, 600); } // show Scriptpad frame function showFrame() { // set global variable by the name "window" globalThis.window = frame; // open new document actionNew(); frame.setVisible(true); } // create and show Scriptpad frame createFrame(); showFrame(); /* * Application object has two fields "frame", "editor" * which are current JFrame and editor and a method * called "addTool" to add new menu item to "Tools" menu. */ return { frame: frame, editor: editor, addTool: addTool }; }; /* * Call the main and store Application object * in a global variable named "application". */ var application = main(); if (this.load == undefined) { function load(file) { var ioPkgs = new JavaImporter(java.io); with (ioPkgs) { var stream = new FileInputStream(file); var bstream = new BufferedInputStream(stream); var reader = new BufferedReader(new InputStreamReader(bstream)); var oldFilename = engine.get(engine.FILENAME); engine.put(engine.FILENAME, file); try { engine.eval(reader, context); } finally { engine.put(engine.FILENAME, oldFilename); } stream.close(); } } load.docString = "loads the given script file"; } /* * Load user specific init file under home dir, if found. */ function loadUserInit() { var home = java.lang.System.getProperty("user.home"); var f = new java.io.File(home, "scriptpad.js"); if (f.exists()) { engine.eval(new java.io.FileReader(f)); } } loadUserInit();