1 /* 2 * Copyright (c) 2016, 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 26 package jdk.internal.jshell.tool; 27 28 import java.io.InputStream; 29 import java.io.PrintStream; 30 import java.util.Locale; 31 import java.util.Map; 32 import java.util.Objects; 33 import java.util.Set; 34 import java.util.prefs.BackingStoreException; 35 import java.util.prefs.Preferences; 36 import jdk.jshell.tool.JavaShellToolBuilder; 37 38 /** 39 * Builder for programmatically building the jshell tool. 40 */ 41 public class JShellToolBuilder implements JavaShellToolBuilder { 42 43 private static final String PREFERENCES_NODE = "tool/JShell"; 44 private InputStream cmdIn = System.in; 45 private InputStream userIn = null; 46 private PrintStream cmdOut = System.out; 47 private PrintStream console = System.out; 48 private PrintStream userOut = System.out; 49 private PrintStream cmdErr = System.err; 50 private PrintStream userErr = System.err; 51 private PersistentStorage prefs = null; 52 private Map<String, String> vars = null; 53 private Locale locale = Locale.getDefault(); 54 private boolean capturePrompt = false; 55 56 /** 57 * Set the input channels. 58 * Default, if not set, {@code in(System.in, null)}. 59 * 60 * @param cmdIn source of command input 61 * @param userIn source of input for running user code, or {@code null} to 62 * be extracted from cmdIn 63 * @return the {@code JavaShellToolBuilder} instance 64 */ 65 @Override 66 public JavaShellToolBuilder in(InputStream cmdIn, InputStream userIn) { 67 this.cmdIn = cmdIn; 68 this.userIn = userIn; 69 return this; 70 } 71 72 /** 73 * Set the output channels. Same as {@code out(output, output, output)}. 74 * Default, if not set, {@code out(System.out)}. 75 * 76 * @param output destination of command feedback, console interaction, and 77 * user code output 78 * @return the {@code JavaShellToolBuilder} instance 79 */ 80 @Override 81 public JavaShellToolBuilder out(PrintStream output) { 82 this.cmdOut = output; 83 this.console = output; 84 this.userOut = output; 85 return this; 86 } 87 88 /** 89 * Set the output channels. 90 * Default, if not set, {@code out(System.out, System.out, System.out)}. 91 * 92 * @param cmdOut destination of command feedback including error messages 93 * for users 94 * @param console destination of console interaction 95 * @param userOut destination of user code output. For example, user snippet 96 * {@code System.out.println("Hello")} when executed {@code Hello} goes to 97 * userOut. 98 * @return the {@code JavaShellToolBuilder} instance 99 */ 100 @Override 101 public JavaShellToolBuilder out(PrintStream cmdOut, PrintStream console, PrintStream userOut) { 102 this.cmdOut = cmdOut; 103 this.console = console; 104 this.userOut = userOut; 105 return this; 106 } 107 108 /** 109 * Set the error channels. Same as {@code err(error, error)}. 110 * Default, if not set, {@code err(System.err)}. 111 * 112 * @param error destination of tool errors, and 113 * user code errors 114 * @return the {@code JavaShellToolBuilder} instance 115 */ 116 @Override 117 public JavaShellToolBuilder err(PrintStream error) { 118 this.cmdErr = error; 119 this.userErr = error; 120 return this; 121 } 122 123 /** 124 * Set the error channels. 125 * Default, if not set, {@code err(System.err, System.err, System.err)}. 126 * 127 * @param cmdErr destination of tool start-up and fatal errors 128 * @param userErr destination of user code error output. 129 * For example, user snippet {@code System.err.println("Oops")} 130 * when executed {@code Oops} goes to userErr. 131 * @return the {@code JavaShellToolBuilder} instance 132 */ 133 @Override 134 public JavaShellToolBuilder err(PrintStream cmdErr, PrintStream userErr) { 135 this.cmdErr = cmdErr; 136 this.userErr = userErr; 137 return this; 138 } 139 140 /** 141 * Set the storage mechanism for persistent information which includes 142 * input history and retained settings. Default if not set is the 143 * tool's standard persistence mechanism. 144 * 145 * @param prefs an instance of {@link java.util.prefs.Preferences} that 146 * is used to retrieve and store persistent information 147 * @return the {@code JavaShellToolBuilder} instance 148 */ 149 @Override 150 public JavaShellToolBuilder persistence(Preferences prefs) { 151 this.prefs = new PreferencesStorage(prefs); 152 return this; 153 } 154 155 /** 156 * Set the storage mechanism for persistent information which includes 157 * input history and retained settings. Default if not set is the 158 * tool's standard persistence mechanism. 159 * 160 * @param prefsMap an instance of {@link java.util.Map} that 161 * is used to retrieve and store persistent information 162 * @return the {@code JavaShellToolBuilder} instance 163 */ 164 @Override 165 public JavaShellToolBuilder persistence(Map<String, String> prefsMap) { 166 this.prefs = new MapStorage(prefsMap); 167 return this; 168 } 169 170 /** 171 * Set the source for environment variables. 172 * Default, if not set, {@code env(System.getenv())}. 173 * 174 * @param vars the Map of environment variable names to values 175 * @return the {@code JavaShellToolBuilder} instance 176 */ 177 @Override 178 public JavaShellToolBuilder env(Map<String, String> vars) { 179 this.vars = vars; 180 return this; 181 } 182 183 /** 184 * Set the locale. 185 * Default, if not set, {@code locale(Locale.getDefault())}. 186 * 187 * @param locale the locale 188 * @return the {@code JavaShellToolBuilder} instance 189 */ 190 @Override 191 public JavaShellToolBuilder locale(Locale locale) { 192 this.locale = locale; 193 return this; 194 } 195 196 /** 197 * Set if the special command capturing prompt override should be used. 198 * Default, if not set, {@code promptCapture(false)}. 199 * 200 * @param capture if {@code true}, basic prompt is the {@code ENQ} 201 * character and continuation prompt is the {@code ACK} character. 202 * If false, prompts are as set with set-up or user {@code /set} commands. 203 * @return the {@code JavaShellToolBuilder} instance 204 */ 205 @Override 206 public JavaShellToolBuilder promptCapture(boolean capture) { 207 this.capturePrompt = capture; 208 return this; 209 } 210 211 /** 212 * Create a tool instance for testing. Not in JavaShellToolBuilder. 213 * 214 * @return the tool instance 215 */ 216 public JShellTool rawTool() { 217 if (prefs == null) { 218 prefs = new PreferencesStorage(Preferences.userRoot().node(PREFERENCES_NODE)); 219 } 220 if (vars == null) { 221 vars = System.getenv(); 222 } 223 JShellTool sh = new JShellTool(cmdIn, cmdOut, cmdErr, console, userIn, 224 userOut, userErr, prefs, vars, locale); 225 sh.testPrompt = capturePrompt; 226 return sh; 227 } 228 229 /** 230 * Run an instance of the Java shell tool as configured by the other methods 231 * in this interface. This call is not destructive, more than one call of 232 * this method may be made from a configured builder. 233 * 234 * @param arguments the command-line arguments (including options), if any 235 * @throws Exception an unexpected fatal exception 236 */ 237 @Override 238 public void run(String... arguments) throws Exception { 239 rawTool().start(arguments); 240 } 241 242 /** 243 * Persistence stored in Preferences. 244 */ 245 private static class PreferencesStorage implements PersistentStorage { 246 247 final Preferences p; 248 249 PreferencesStorage(Preferences p) { 250 this.p = p; 251 } 252 253 @Override 254 public void clear() { 255 try { 256 p.clear(); 257 } catch (BackingStoreException ex) { 258 throw new IllegalStateException(ex); 259 } 260 } 261 262 @Override 263 public String[] keys() { 264 try { 265 return p.keys(); 266 } catch (BackingStoreException ex) { 267 throw new IllegalStateException(ex); 268 } 269 } 270 271 @Override 272 public String get(String key) { 273 return p.get(key, null); 274 } 275 276 @Override 277 public void put(String key, String value) { 278 p.put(key, value); 279 } 280 281 @Override 282 public void remove(String key) { 283 p.remove(key); 284 } 285 286 @Override 287 public void flush() { 288 try { 289 p.flush(); 290 } catch (BackingStoreException ex) { 291 throw new IllegalStateException(ex); 292 } 293 } 294 } 295 296 /** 297 * Persistence stored in a Map. 298 */ 299 private static class MapStorage implements PersistentStorage { 300 301 final Map<String, String> map; 302 303 MapStorage(Map<String, String> map) { 304 this.map = map; 305 } 306 307 @Override 308 public void clear() { 309 310 try { 311 map.clear(); 312 } catch (UnsupportedOperationException ex) { 313 throw new IllegalStateException(ex); 314 } 315 } 316 317 @Override 318 public String[] keys() { 319 Set<String> ks = map.keySet(); 320 return ks.toArray(new String[ks.size()]); 321 } 322 323 @Override 324 public String get(String key) { 325 Objects.requireNonNull(key); 326 return map.get(key); 327 } 328 329 @Override 330 public void put(String key, String value) { 331 Objects.requireNonNull(key); 332 Objects.requireNonNull(value); 333 map.put(key, value); 334 } 335 336 @Override 337 public void remove(String key) { 338 Objects.requireNonNull(key); 339 map.remove(key); 340 } 341 342 @Override 343 public void flush() { 344 // no-op always up-to-date 345 } 346 } 347 348 }