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. The exit code from 233 * the Java shell tool is ignored. 234 * 235 * @param arguments the command-line arguments (including options), if any 236 * @throws Exception an unexpected fatal exception 237 */ 238 @Override 239 public void run(String... arguments) throws Exception { 240 rawTool().start(arguments); 241 } 242 243 /** 244 * Run an instance of the Java shell tool as configured by the other methods 245 * in this interface. This call is not destructive, more than one call of 246 * this method may be made from a configured builder. 247 * 248 * @param arguments the command-line arguments (including options), if any 249 * @throws Exception an unexpected fatal exception 250 * @return the exit code 251 */ 252 @Override 253 public int start(String... arguments) throws Exception { 254 return rawTool().start(arguments); 255 } 256 257 /** 258 * Persistence stored in Preferences. 259 */ 260 private static class PreferencesStorage implements PersistentStorage { 261 262 final Preferences p; 263 264 PreferencesStorage(Preferences p) { 265 this.p = p; 266 } 267 268 @Override 269 public void clear() { 270 try { 271 p.clear(); 272 } catch (BackingStoreException ex) { 273 throw new IllegalStateException(ex); 274 } 275 } 276 277 @Override 278 public String[] keys() { 279 try { 280 return p.keys(); 281 } catch (BackingStoreException ex) { 282 throw new IllegalStateException(ex); 283 } 284 } 285 286 @Override 287 public String get(String key) { 288 return p.get(key, null); 289 } 290 291 @Override 292 public void put(String key, String value) { 293 p.put(key, value); 294 } 295 296 @Override 297 public void remove(String key) { 298 p.remove(key); 299 } 300 301 @Override 302 public void flush() { 303 try { 304 p.flush(); 305 } catch (BackingStoreException ex) { 306 throw new IllegalStateException(ex); 307 } 308 } 309 } 310 311 /** 312 * Persistence stored in a Map. 313 */ 314 private static class MapStorage implements PersistentStorage { 315 316 final Map<String, String> map; 317 318 MapStorage(Map<String, String> map) { 319 this.map = map; 320 } 321 322 @Override 323 public void clear() { 324 325 try { 326 map.clear(); 327 } catch (UnsupportedOperationException ex) { 328 throw new IllegalStateException(ex); 329 } 330 } 331 332 @Override 333 public String[] keys() { 334 Set<String> ks = map.keySet(); 335 return ks.toArray(new String[ks.size()]); 336 } 337 338 @Override 339 public String get(String key) { 340 Objects.requireNonNull(key); 341 return map.get(key); 342 } 343 344 @Override 345 public void put(String key, String value) { 346 Objects.requireNonNull(key); 347 Objects.requireNonNull(value); 348 map.put(key, value); 349 } 350 351 @Override 352 public void remove(String key) { 353 Objects.requireNonNull(key); 354 map.remove(key); 355 } 356 357 @Override 358 public void flush() { 359 // no-op always up-to-date 360 } 361 } 362 363 }