8134562: jjs history object should have methods to save/load history to/from given file and also allow reexecution of commands by a call
authorsundar
Thu, 27 Aug 2015 14:35:06 +0530
changeset 32324 b021cc36fd65
parent 32323 ec97bf27ad3b
child 32325 68c59ce3ed28
8134562: jjs history object should have methods to save/load history to/from given file and also allow reexecution of commands by a call Reviewed-by: hannesw, attila
nashorn/src/jdk.scripting.nashorn.shell/share/classes/jdk/nashorn/tools/jjs/EditObject.java
nashorn/src/jdk.scripting.nashorn.shell/share/classes/jdk/nashorn/tools/jjs/HistoryObject.java
nashorn/src/jdk.scripting.nashorn.shell/share/classes/jdk/nashorn/tools/jjs/Main.java
--- a/nashorn/src/jdk.scripting.nashorn.shell/share/classes/jdk/nashorn/tools/jjs/EditObject.java	Wed Aug 26 20:30:34 2015 +0200
+++ b/nashorn/src/jdk.scripting.nashorn.shell/share/classes/jdk/nashorn/tools/jjs/EditObject.java	Thu Aug 27 14:35:06 2015 +0530
@@ -45,16 +45,16 @@
         props = Collections.unmodifiableSet(s);
     }
 
+    private final Console console;
     private final Consumer<String> errorHandler;
     private final Consumer<String> evaluator;
-    private final Console console;
     private String editor;
 
-    EditObject(final Consumer<String> errorHandler, final Consumer<String> evaluator,
-            final Console console) {
+    EditObject(final Console console, final Consumer<String> errorHandler,
+            final Consumer<String> evaluator) {
+        this.console = console;
         this.errorHandler = errorHandler;
         this.evaluator = evaluator;
-        this.console = console;
     }
 
     @Override
--- a/nashorn/src/jdk.scripting.nashorn.shell/share/classes/jdk/nashorn/tools/jjs/HistoryObject.java	Wed Aug 26 20:30:34 2015 +0200
+++ b/nashorn/src/jdk.scripting.nashorn.shell/share/classes/jdk/nashorn/tools/jjs/HistoryObject.java	Thu Aug 27 14:35:06 2015 +0530
@@ -25,10 +25,15 @@
 
 package jdk.nashorn.tools.jjs;
 
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
 import java.io.IOException;
+import java.io.PrintWriter;
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.Set;
+import java.util.function.Consumer;
 import java.util.function.Function;
 import java.util.function.Supplier;
 import jdk.internal.jline.console.history.FileHistory;
@@ -36,6 +41,7 @@
 import jdk.nashorn.api.scripting.AbstractJSObject;
 import jdk.nashorn.api.scripting.JSObject;
 import jdk.nashorn.internal.runtime.JSType;
+import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
 import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
 
 /*
@@ -47,16 +53,51 @@
         final HashSet<String> s = new HashSet<>();
         s.add("clear");
         s.add("forEach");
+        s.add("load");
         s.add("print");
+        s.add("save");
         s.add("size");
         s.add("toString");
         props = Collections.unmodifiableSet(s);
     }
 
     private final FileHistory hist;
+    private final PrintWriter err;
+    private final Consumer<String> evaluator;
 
-    HistoryObject(final FileHistory hist) {
+    HistoryObject(final FileHistory hist, final PrintWriter err,
+            final Consumer<String> evaluator) {
         this.hist = hist;
+        this.err = err;
+        this.evaluator = evaluator;
+    }
+
+    @Override
+    public boolean isFunction() {
+        return true;
+    }
+
+    @Override
+    public Object call(final Object thiz, final Object... args) {
+        if (args.length > 0) {
+            int index = JSType.toInteger(args[0]);
+            if (index < 0) {
+                index += (hist.size() - 1);
+            } else {
+                index--;
+            }
+
+            if (index >= 0 && index < (hist.size() - 1)) {
+                final CharSequence src = hist.get(index);
+                hist.replace(src);
+                err.println(src);
+                evaluator.accept(src.toString());
+            } else {
+                hist.removeLast();
+                err.println("no history entry @ " + (index + 1));
+            }
+        }
+        return UNDEFINED;
     }
 
     @Override
@@ -66,8 +107,12 @@
                 return (Runnable)hist::clear;
             case "forEach":
                 return (Function<JSObject, Object>)this::iterate;
+            case "load":
+                return (Consumer<Object>)this::load;
             case "print":
                 return (Runnable)this::print;
+            case "save":
+                return (Consumer<Object>)this::save;
             case "size":
                 return hist.size();
             case "toString":
@@ -98,9 +143,32 @@
         return props;
     }
 
+    private void save(final Object obj) {
+        final File file = getFile(obj);
+        try (final PrintWriter pw = new PrintWriter(file)) {
+            for (History.Entry e : hist) {
+                pw.println(e.value());
+            }
+        } catch (final IOException exp) {
+            throw new RuntimeException(exp);
+        }
+    }
+
+    private void load(final Object obj) {
+        final File file = getFile(obj);
+        String item = null;
+        try (final BufferedReader r = new BufferedReader(new FileReader(file))) {
+            while ((item = r.readLine()) != null) {
+                hist.add(item);
+            }
+        } catch (final IOException exp) {
+            throw new RuntimeException(exp);
+        }
+    }
+
     private void print() {
         for (History.Entry e : hist) {
-            System.out.println(e.value());
+            System.out.printf("%3d %s\n", e.index() + 1, e.value());
         }
     }
 
@@ -112,4 +180,17 @@
         }
         return UNDEFINED;
     }
+
+    private static File getFile(final Object obj) {
+        File file = null;
+        if (obj instanceof String) {
+            file = new File((String)obj);
+        } else if (obj instanceof File) {
+            file = (File)obj;
+        } else {
+            throw typeError("not.a.file", JSType.toString(obj));
+        }
+
+        return file;
+    }
 }
--- a/nashorn/src/jdk.scripting.nashorn.shell/share/classes/jdk/nashorn/tools/jjs/Main.java	Wed Aug 26 20:30:34 2015 +0200
+++ b/nashorn/src/jdk.scripting.nashorn.shell/share/classes/jdk/nashorn/tools/jjs/Main.java	Thu Aug 27 14:35:06 2015 +0530
@@ -33,6 +33,7 @@
 import java.io.IOException;
 import java.io.OutputStream;
 import java.io.PrintWriter;
+import java.util.function.Consumer;
 import jdk.internal.jline.console.completer.Completer;
 import jdk.internal.jline.console.UserInterruptException;
 import jdk.nashorn.api.scripting.NashornException;
@@ -116,26 +117,27 @@
             global.addShellBuiltins();
 
             if (System.getSecurityManager() == null) {
+                final Consumer<String> evaluator = str -> {
+                    // could be called from different thread (GUI), we need to handle Context set/reset
+                    final Global _oldGlobal = Context.getGlobal();
+                    final boolean _globalChanged = (oldGlobal != global);
+                    if (_globalChanged) {
+                        Context.setGlobal(global);
+                    }
+                    try {
+                        evalImpl(context, global, str, err, env._dump_on_error);
+                    } finally {
+                        if (_globalChanged) {
+                            Context.setGlobal(_oldGlobal);
+                        }
+                    }
+                };
+
                 // expose history object for reflecting on command line history
-                global.addOwnProperty("history", Property.NOT_ENUMERABLE, new HistoryObject(in.getHistory()));
+                global.addOwnProperty("history", Property.NOT_ENUMERABLE, new HistoryObject(in.getHistory(), err, evaluator));
 
                 // 'edit' command
-                global.addOwnProperty("edit", Property.NOT_ENUMERABLE, new EditObject(err::println,
-                    str -> {
-                        // could be called from different thread (GUI), we need to handle Context set/reset
-                        final Global _oldGlobal = Context.getGlobal();
-                        final boolean _globalChanged = (oldGlobal != global);
-                        if (_globalChanged) {
-                            Context.setGlobal(global);
-                        }
-                        try {
-                            evalImpl(context, global, str, err, env._dump_on_error);
-                        } finally {
-                            if (_globalChanged) {
-                                Context.setGlobal(_oldGlobal);
-                            }
-                        }
-                    }, in));
+                global.addOwnProperty("edit", Property.NOT_ENUMERABLE, new EditObject(in, err::println, evaluator));
             }
 
             while (true) {