HistoryObject.java revision 1695:be28ce2f1054
1/* 2 * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26package jdk.nashorn.tools.jjs; 27 28import java.io.BufferedReader; 29import java.io.File; 30import java.io.FileReader; 31import java.io.IOException; 32import java.io.PrintWriter; 33import java.util.Collections; 34import java.util.HashSet; 35import java.util.Set; 36import java.util.function.Consumer; 37import java.util.function.Function; 38import java.util.function.Supplier; 39import jdk.internal.jline.console.history.History; 40import jdk.nashorn.api.scripting.AbstractJSObject; 41import jdk.nashorn.api.scripting.JSObject; 42import jdk.nashorn.internal.runtime.JSType; 43import static jdk.nashorn.internal.runtime.ECMAErrors.typeError; 44import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED; 45 46/* 47 * A script friendly object that exposes history of commands to scripts. 48 */ 49final class HistoryObject extends AbstractJSObject { 50 private static final Set<String> props; 51 static { 52 final HashSet<String> s = new HashSet<>(); 53 s.add("clear"); 54 s.add("forEach"); 55 s.add("load"); 56 s.add("print"); 57 s.add("save"); 58 s.add("size"); 59 s.add("toString"); 60 props = Collections.unmodifiableSet(s); 61 } 62 63 private final History hist; 64 private final PrintWriter err; 65 private final Consumer<String> evaluator; 66 67 HistoryObject(final History hist, final PrintWriter err, 68 final Consumer<String> evaluator) { 69 this.hist = hist; 70 this.err = err; 71 this.evaluator = evaluator; 72 } 73 74 @Override 75 public boolean isFunction() { 76 return true; 77 } 78 79 @Override 80 public Object call(final Object thiz, final Object... args) { 81 if (args.length > 0) { 82 int index = JSType.toInteger(args[0]); 83 if (index < 0) { 84 index += (hist.size() - 1); 85 } else { 86 index--; 87 } 88 89 if (index >= 0 && index < (hist.size() - 1)) { 90 final CharSequence src = hist.get(index); 91 hist.replace(src); 92 err.println(src); 93 evaluator.accept(src.toString()); 94 } else { 95 hist.removeLast(); 96 err.println("no history entry @ " + (index + 1)); 97 } 98 } 99 return UNDEFINED; 100 } 101 102 @Override 103 public Object getMember(final String name) { 104 switch (name) { 105 case "clear": 106 return (Runnable)hist::clear; 107 case "forEach": 108 return (Function<JSObject, Object>)this::iterate; 109 case "load": 110 return (Consumer<Object>)this::load; 111 case "print": 112 return (Runnable)this::print; 113 case "save": 114 return (Consumer<Object>)this::save; 115 case "size": 116 return hist.size(); 117 case "toString": 118 return (Supplier<String>)this::toString; 119 } 120 return UNDEFINED; 121 } 122 123 @Override 124 public Object getDefaultValue(final Class<?> hint) { 125 if (hint == String.class) { 126 return toString(); 127 } 128 return UNDEFINED; 129 } 130 131 @Override 132 public String toString() { 133 final StringBuilder buf = new StringBuilder(); 134 for (History.Entry e : hist) { 135 buf.append(e.value()).append('\n'); 136 } 137 return buf.toString(); 138 } 139 140 @Override 141 public Set<String> keySet() { 142 return props; 143 } 144 145 private void save(final Object obj) { 146 final File file = getFile(obj); 147 try (final PrintWriter pw = new PrintWriter(file)) { 148 for (History.Entry e : hist) { 149 pw.println(e.value()); 150 } 151 } catch (final IOException exp) { 152 throw new RuntimeException(exp); 153 } 154 } 155 156 private void load(final Object obj) { 157 final File file = getFile(obj); 158 String item = null; 159 try (final BufferedReader r = new BufferedReader(new FileReader(file))) { 160 while ((item = r.readLine()) != null) { 161 hist.add(item); 162 } 163 } catch (final IOException exp) { 164 throw new RuntimeException(exp); 165 } 166 } 167 168 private void print() { 169 for (History.Entry e : hist) { 170 System.out.printf("%3d %s\n", e.index() + 1, e.value()); 171 } 172 } 173 174 private Object iterate(final JSObject func) { 175 for (History.Entry e : hist) { 176 if (JSType.toBoolean(func.call(this, e.value().toString()))) { 177 break; // return true from callback to skip iteration 178 } 179 } 180 return UNDEFINED; 181 } 182 183 private static File getFile(final Object obj) { 184 File file = null; 185 if (obj instanceof String) { 186 file = new File((String)obj); 187 } else if (obj instanceof File) { 188 file = (File)obj; 189 } else { 190 throw typeError("not.a.file", JSType.toString(obj)); 191 } 192 193 return file; 194 } 195} 196