From 52a154fa4407f155cc84d5a7498ff145c22d67d6 Mon Sep 17 00:00:00 2001
From: Viktor Kuchyn
+ * Example 1: Any argument of the command line can
+ * use file completion.
+ *
+ *
+ * Example 2: The first argument of the command line
+ * can be completed with any of "foo", "bar", or "baz", and remaining
+ * arguments can be completed with a file name.
+ *
+ *
+ * When the argument index is past the last embedded completors, the last
+ * completors is always used. To disable this behavior, have the last
+ * completor be a {@link NullCompletor}. For example:
+ *
+ * TODO: handle argument quoting and escape characters
+ *
+ * A {@link CompletionHandler} that deals with multiple distinct completions
+ * by cycling through each one every time tab is pressed. This
+ * mimics the behavior of the
+ * editline
+ * library.
+ * This class is currently a stub; it does nothing
+ * A {@link CompletionHandler} that deals with multiple distinct completions
+ * by outputting the complete list of possibilities to the console. This
+ * mimics the behavior of the
+ * readline
+ * library.
+ *
+ * Set the echo character. For example, to have "*" entered when a password
+ * is typed:
+ *
+ * Setting the character to
+ *
+ *
+ * mode description
+ * ----------------------------------------
+ * 0 40 x 148 x 25 monochrome (text)
+ * 1 40 x 148 x 25 color (text)
+ * 2 80 x 148 x 25 monochrome (text)
+ * 3 80 x 148 x 25 color (text)
+ * 4 320 x 148 x 200 4-color (graphics)
+ * 5 320 x 148 x 200 monochrome (graphics)
+ * 6 640 x 148 x 200 monochrome (graphics)
+ * 7 Enables line wrapping
+ * 13 320 x 148 x 200 color (graphics)
+ * 14 640 x 148 x 200 color (16-color graphics)
+ * 15 640 x 148 x 350 monochrome (2-color graphics)
+ * 16 640 x 148 x 350 color (16-color graphics)
+ * 17 640 x 148 x 480 monochrome (2-color graphics)
+ * 18 640 x 148 x 480 color (16-color graphics)
+ * 19 320 x 148 x 200 color (256-color graphics)
+ *
+ */
+ public static String setmode(final int mode) {
+ return ESC + "[=" + mode + "h";
+ }
+
+ /**
+ * Same as setmode () except for mode = 7, which disables line
+ * wrapping (useful for writing the right-most column without
+ * scrolling to the next line).
+ */
+ public static String resetmode(final int mode) {
+ return ESC + "[=" + mode + "l";
+ }
+
+ /**
+ * Clears the screen and moves the cursor to the home postition.
+ */
+ public static String clrscr() {
+ return ESC + "[2J";
+ }
+
+ /**
+ * Removes all characters from the current cursor position until
+ * the end of the line.
+ */
+ public static String clreol() {
+ return ESC + "[K";
+ }
+
+ /**
+ * Moves the cursor n positions to the left. If n is greater or
+ * equal to the current cursor column, the cursor is moved to the
+ * first column.
+ */
+ public static String left(final int n) {
+ return ESC + "[" + n + "D";
+ }
+
+ /**
+ * Moves the cursor n positions to the right. If n plus the current
+ * cursor column is greater than the rightmost column, the cursor
+ * is moved to the rightmost column.
+ */
+ public static String right(final int n) {
+ return ESC + "[" + n + "C";
+ }
+
+ /**
+ * Moves the cursor n rows up without changing the current column.
+ * If n is greater than or equal to the current row, the cursor is
+ * placed in the first row.
+ */
+ public static String up(final int n) {
+ return ESC + "[" + n + "A";
+ }
+
+ /**
+ * Moves the cursor n rows down. If n plus the current row is greater
+ * than the bottom row, the cursor is moved to the bottom row.
+ */
+ public static String down(final int n) {
+ return ESC + "[" + n + "B";
+ }
+
+ /*
+ * Moves the cursor to the given row and column. (1,1) represents
+ * the upper left corner. The lower right corner of a usual DOS
+ * screen is (25, 80).
+ */
+ public static String gotoxy(final int row, final int column) {
+ return ESC + "[" + row + ";" + column + "H";
+ }
+
+ /**
+ * Saves the current cursor position.
+ */
+ public static String save() {
+ return ESC + "[s";
+ }
+
+ /**
+ * Restores the saved cursor position.
+ */
+ public static String restore() {
+ return ESC + "[u";
+ }
+
+ /**
+ * Sets the character attribute. It will be
+ * one of the following character attributes:
+ *
+ *
+ * Text attributes
+ * 0 All attributes off
+ * 1 Bold on
+ * 4 Underscore (on monochrome display adapter only)
+ * 5 Blink on
+ * 7 Reverse video on
+ * 8 Concealed on
+ *
+ * Foreground colors
+ * 30 Black
+ * 31 Red
+ * 32 Green
+ * 33 Yellow
+ * 34 Blue
+ * 35 Magenta
+ * 36 Cyan
+ * 37 White
+ *
+ * Background colors
+ * 40 Black
+ * 41 Red
+ * 42 Green
+ * 43 Yellow
+ * 44 Blue
+ * 45 Magenta
+ * 46 Cyan
+ * 47 White
+ *
+ *
+ * The attributes remain in effect until the next attribute command
+ * is sent.
+ */
+ public static String attrib(final int attr) {
+ return ESC + "[" + attr + "m";
+ }
+
+ /**
+ * Sets the key with the given code to the given value. code must be
+ * derived from the following table, value must
+ * be any semicolon-separated
+ * combination of String (enclosed in double quotes) and numeric values.
+ * For example, to set F1 to the String "Hello F1", followed by a CRLF
+ * sequence, one can use: ANSI.setkey ("0;59", "\"Hello F1\";13;10").
+ * Heres's the table of key values:
+ *
+ * Key Code SHIFT+code CTRL+code ALT+code
+ * ---------------------------------------------------------------
+ * F1 0;59 0;84 0;94 0;104
+ * F2 0;60 0;85 0;95 0;105
+ * F3 0;61 0;86 0;96 0;106
+ * F4 0;62 0;87 0;97 0;107
+ * F5 0;63 0;88 0;98 0;108
+ * F6 0;64 0;89 0;99 0;109
+ * F7 0;65 0;90 0;100 0;110
+ * F8 0;66 0;91 0;101 0;111
+ * F9 0;67 0;92 0;102 0;112
+ * F10 0;68 0;93 0;103 0;113
+ * F11 0;133 0;135 0;137 0;139
+ * F12 0;134 0;136 0;138 0;140
+ * HOME (num keypad) 0;71 55 0;119 --
+ * UP ARROW (num keypad) 0;72 56 (0;141) --
+ * PAGE UP (num keypad) 0;73 57 0;132 --
+ * LEFT ARROW (num keypad) 0;75 52 0;115 --
+ * RIGHT ARROW (num keypad) 0;77 54 0;116 --
+ * END (num keypad) 0;79 49 0;117 --
+ * DOWN ARROW (num keypad) 0;80 50 (0;145) --
+ * PAGE DOWN (num keypad) 0;81 51 0;118 --
+ * INSERT (num keypad) 0;82 48 (0;146) --
+ * DELETE (num keypad) 0;83 46 (0;147) --
+ * HOME (224;71) (224;71) (224;119) (224;151)
+ * UP ARROW (224;72) (224;72) (224;141) (224;152)
+ * PAGE UP (224;73) (224;73) (224;132) (224;153)
+ * LEFT ARROW (224;75) (224;75) (224;115) (224;155)
+ * RIGHT ARROW (224;77) (224;77) (224;116) (224;157)
+ * END (224;79) (224;79) (224;117) (224;159)
+ * DOWN ARROW (224;80) (224;80) (224;145) (224;154)
+ * PAGE DOWN (224;81) (224;81) (224;118) (224;161)
+ * INSERT (224;82) (224;82) (224;146) (224;162)
+ * DELETE (224;83) (224;83) (224;147) (224;163)
+ * PRINT SCREEN -- -- 0;114 --
+ * PAUSE/BREAK -- -- 0;0 --
+ * BACKSPACE 8 8 127 (0)
+ * ENTER 13 -- 10 (0
+ * TAB 9 0;15 (0;148) (0;165)
+ * NULL 0;3 -- -- --
+ * A 97 65 1 0;30
+ * B 98 66 2 0;48
+ * C 99 66 3 0;46
+ * D 100 68 4 0;32
+ * E 101 69 5 0;18
+ * F 102 70 6 0;33
+ * G 103 71 7 0;34
+ * H 104 72 8 0;35
+ * I 105 73 9 0;23
+ * J 106 74 10 0;36
+ * K 107 75 11 0;37
+ * L 108 76 12 0;38
+ * M 109 77 13 0;50
+ * N 110 78 14 0;49
+ * O 111 79 15 0;24
+ * P 112 80 16 0;25
+ * Q 113 81 17 0;16
+ * R 114 82 18 0;19
+ * S 115 83 19 0;31
+ * T 116 84 20 0;20
+ * U 117 85 21 0;22
+ * V 118 86 22 0;47
+ * W 119 87 23 0;17
+ * X 120 88 24 0;45
+ * Y 121 89 25 0;21
+ * Z 122 90 26 0;44
+ * 1 49 33 -- 0;120
+ * 2 50 64 0 0;121
+ * 3 51 35 -- 0;122
+ * 4 52 36 -- 0;123
+ * 5 53 37 -- 0;124
+ * 6 54 94 30 0;125
+ * 7 55 38 -- 0;126
+ * 8 56 42 -- 0;126
+ * 9 57 40 -- 0;127
+ * 0 48 41 -- 0;129
+ * - 45 95 31 0;130
+ * = 61 43 --- 0;131
+ * [ 91 123 27 0;26
+ * ] 93 125 29 0;27
+ * 92 124 28 0;43
+ * ; 59 58 -- 0;39
+ * ' 39 34 -- 0;40
+ * , 44 60 -- 0;51
+ * . 46 62 -- 0;52
+ * / 47 63 -- 0;53
+ * ` 96 126 -- (0;41)
+ * ENTER (keypad) 13 -- 10 (0;166)
+ * / (keypad) 47 47 (0;142) (0;74)
+ * * (keypad) 42 (0;144) (0;78) --
+ * - (keypad) 45 45 (0;149) (0;164)
+ * + (keypad) 43 43 (0;150) (0;55)
+ * 5 (keypad) (0;76) 53 (0;143) --
+ */
+ public static String setkey(final String code, final String value) {
+ return ESC + "[" + code + ";" + value + "p";
+ }
+ }
+
+ public static void main(final String[] args) throws Exception {
+ // sequence, one can use: ANSI.setkey ("0;59", "\"Hello F1\";13;10").
+ BufferedReader reader =
+ new BufferedReader(new InputStreamReader(System.in));
+ System.out.print(ANSICodes.setkey("97", "97;98;99;13")
+ + ANSICodes.attrib(ANSICodes.OFF));
+ System.out.flush();
+
+ String line;
+
+ while ((line = reader.readLine()) != null) {
+ System.out.println("GOT: " + line);
+ }
+ }
+}
diff --git a/lib/src/src/main/java/jline/ArgumentCompletor.java b/lib/src/src/main/java/jline/ArgumentCompletor.java
new file mode 100644
index 0000000..5493ad8
--- /dev/null
+++ b/lib/src/src/main/java/jline/ArgumentCompletor.java
@@ -0,0 +1,439 @@
+/*
+ * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved.
+ *
+ * This software is distributable under the BSD license. See the terms of the
+ * BSD license in the documentation provided with this software.
+ */
+package jline;
+
+import java.util.*;
+
+/**
+ * A {@link Completor} implementation that invokes a child completor
+ * using the appropriate separator argument. This
+ * can be used instead of the individual completors having to
+ * know about argument parsing semantics.
+ *
+ * consoleReader.addCompletor (new ArgumentCompletor (
+ * new {@link FileNameCompletor} ()))
+ *
+ *
+ * consoleReader.addCompletor (new ArgumentCompletor (
+ * new {@link SimpleCompletor} (new String [] { "foo", "bar", "baz"})));
+ * consoleReader.addCompletor (new ArgumentCompletor (
+ * new {@link FileNameCompletor} ()));
+ *
+ *
+ *
+ * consoleReader.addCompletor (new ArgumentCompletor (
+ * new {@link SimpleCompletor} (new String [] { "foo", "bar", "baz"}),
+ * new {@link SimpleCompletor} (new String [] { "xxx", "yyy", "xxx"}),
+ * new {@link NullCompletor}
+ * ));
+ *
+ *
+ *
+ *
+ * @author Marc Prud'hommeaux
+ */
+public class CandidateListCompletionHandler implements CompletionHandler {
+ private static ResourceBundle loc = ResourceBundle.
+ getBundle(CandidateListCompletionHandler.class.getName());
+
+ private boolean eagerNewlines = true;
+
+ public void setAlwaysIncludeNewline(boolean eagerNewlines) {
+ this.eagerNewlines = eagerNewlines;
+ }
+
+ public boolean complete(final ConsoleReader reader, final List candidates,
+ final int pos) throws IOException {
+ CursorBuffer buf = reader.getCursorBuffer();
+
+ // if there is only one completion, then fill in the buffer
+ if (candidates.size() == 1) {
+ String value = candidates.get(0).toString();
+
+ // fail if the only candidate is the same as the current buffer
+ if (value.equals(buf.toString())) {
+ return false;
+ }
+
+ setBuffer(reader, value, pos);
+
+ return true;
+ } else if (candidates.size() > 1) {
+ String value = getUnambiguousCompletions(candidates);
+ String bufString = buf.toString();
+ setBuffer(reader, value, pos);
+ }
+
+ if (eagerNewlines)
+ reader.printNewline();
+ printCandidates(reader, candidates, eagerNewlines);
+
+ // redraw the current console buffer
+ reader.drawLine();
+
+ return true;
+ }
+
+ public static void setBuffer(ConsoleReader reader, String value, int offset)
+ throws IOException {
+ while ((reader.getCursorBuffer().cursor > offset)
+ && reader.backspace()) {
+ ;
+ }
+
+ reader.putString(value);
+ reader.setCursorPosition(offset + value.length());
+ }
+
+ /**
+ * Print out the candidates. If the size of the candidates
+ * is greated than the {@link getAutoprintThreshhold},
+ * they prompt with aq warning.
+ *
+ * @param candidates the list of candidates to print
+ */
+ public static final void printCandidates(ConsoleReader reader,
+ Collection candidates, boolean eagerNewlines)
+ throws IOException {
+ Set distinct = new HashSet(candidates);
+
+ if (distinct.size() > reader.getAutoprintThreshhold()) {
+ if (!eagerNewlines)
+ reader.printNewline();
+ reader.printString(MessageFormat.format
+ (loc.getString("display-candidates"), new Object[] {
+ new Integer(candidates .size())
+ }) + " ");
+
+ reader.flushConsole();
+
+ int c;
+
+ String noOpt = loc.getString("display-candidates-no");
+ String yesOpt = loc.getString("display-candidates-yes");
+
+ while ((c = reader.readCharacter(new char[] {
+ yesOpt.charAt(0), noOpt.charAt(0) })) != -1) {
+ if (noOpt.startsWith
+ (new String(new char[] { (char) c }))) {
+ reader.printNewline();
+ return;
+ } else if (yesOpt.startsWith
+ (new String(new char[] { (char) c }))) {
+ break;
+ } else {
+ reader.beep();
+ }
+ }
+ }
+
+ // copy the values and make them distinct, without otherwise
+ // affecting the ordering. Only do it if the sizes differ.
+ if (distinct.size() != candidates.size()) {
+ Collection copy = new ArrayList();
+
+ for (Iterator i = candidates.iterator(); i.hasNext();) {
+ Object next = i.next();
+
+ if (!(copy.contains(next))) {
+ copy.add(next);
+ }
+ }
+
+ candidates = copy;
+ }
+
+ reader.printNewline();
+ reader.printColumns(candidates);
+ }
+
+ /**
+ * Returns a root that matches all the {@link String} elements
+ * of the specified {@link List}, or null if there are
+ * no commalities. For example, if the list contains
+ * foobar, foobaz, foobuz, the
+ * method will return foob.
+ */
+ private final String getUnambiguousCompletions(final List candidates) {
+ if ((candidates == null) || (candidates.size() == 0)) {
+ return null;
+ }
+
+ // convert to an array for speed
+ String[] strings =
+ (String[]) candidates.toArray(new String[candidates.size()]);
+
+ String first = strings[0];
+ StringBuffer candidate = new StringBuffer();
+
+ for (int i = 0; i < first.length(); i++) {
+ if (startsWith(first.substring(0, i + 1), strings)) {
+ candidate.append(first.charAt(i));
+ } else {
+ break;
+ }
+ }
+
+ return candidate.toString();
+ }
+
+ /**
+ * @return true is all the elements of candidates
+ * start with starts
+ */
+ private final boolean startsWith(final String starts,
+ final String[] candidates) {
+ for (int i = 0; i < candidates.length; i++) {
+ if (!candidates[i].startsWith(starts)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+}
diff --git a/lib/src/src/main/java/jline/ClassNameCompletor.java b/lib/src/src/main/java/jline/ClassNameCompletor.java
new file mode 100644
index 0000000..3ef5802
--- /dev/null
+++ b/lib/src/src/main/java/jline/ClassNameCompletor.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved.
+ *
+ * This software is distributable under the BSD license. See the terms of the
+ * BSD license in the documentation provided with this software.
+ */
+package jline;
+
+import java.io.*;
+import java.net.*;
+import java.util.*;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+
+/**
+ * A Completor implementation that completes java class names. By default,
+ * it scans the java class path to locate all the classes.
+ *
+ * @author Marc Prud'hommeaux
+ */
+public class ClassNameCompletor extends SimpleCompletor {
+
+ /**
+ * Complete candidates using all the classes available in the
+ * java CLASSPATH.
+ */
+ public ClassNameCompletor() throws IOException {
+ this(null);
+ }
+
+ public ClassNameCompletor(final SimpleCompletorFilter filter)
+ throws IOException {
+ super(getClassNames(), filter);
+ setDelimiter(".");
+ }
+
+ public static String[] getClassNames() throws IOException {
+ Set urls = new HashSet();
+
+ for (ClassLoader loader = ClassNameCompletor.class
+ .getClassLoader(); loader != null;
+ loader = loader.getParent()) {
+ if (!(loader instanceof URLClassLoader)) {
+ continue;
+ }
+
+ urls.addAll(Arrays.asList(((URLClassLoader) loader).getURLs()));
+ }
+
+ // Now add the URL that holds java.lang.String. This is because
+ // some JVMs do not report the core classes jar in the list of
+ // class loaders.
+ Class[] systemClasses = new Class[] {
+ String.class, javax.swing.JFrame.class
+ };
+
+ for (int i = 0; i < systemClasses.length; i++) {
+ URL classURL = systemClasses[i].getResource("/"
+ + systemClasses[i].getName() .replace('.', '/') + ".class");
+
+ if (classURL != null) {
+ URLConnection uc = (URLConnection) classURL.openConnection();
+
+ if (uc instanceof JarURLConnection) {
+ urls.add(((JarURLConnection) uc).getJarFileURL());
+ }
+ }
+ }
+
+ Set classes = new HashSet();
+
+ for (Iterator i = urls.iterator(); i.hasNext();) {
+ URL url = (URL) i.next();
+ File file = new File(url.getFile());
+
+ if (file.isDirectory()) {
+ Set files = getClassFiles(file.getAbsolutePath(),
+ new HashSet(), file, new int[] { 200 });
+ classes.addAll(files);
+
+ continue;
+ }
+
+ if ((file == null) || !file.isFile()) // TODO: handle directories
+ {
+ continue;
+ }
+ if (!file.toString().endsWith (".jar"))
+ continue;
+
+ JarFile jf = new JarFile(file);
+
+ for (Enumeration e = jf.entries(); e.hasMoreElements();) {
+ JarEntry entry = (JarEntry) e.nextElement();
+
+ if (entry == null) {
+ continue;
+ }
+
+ String name = entry.getName();
+
+ if (!name.endsWith(".class")) // only use class files
+ {
+ continue;
+ }
+
+ classes.add(name);
+ }
+ }
+
+ // now filter classes by changing "/" to "." and trimming the
+ // trailing ".class"
+ Set classNames = new TreeSet();
+
+ for (Iterator i = classes.iterator(); i.hasNext();) {
+ String name = (String) i.next();
+ classNames.add(name.replace('/', '.').
+ substring(0, name.length() - 6));
+ }
+
+ return (String[]) classNames.toArray(new String[classNames.size()]);
+ }
+
+ private static Set getClassFiles(String root, Set holder, File directory,
+ int[] maxDirectories) {
+ // we have passed the maximum number of directories to scan
+ if (maxDirectories[0]-- < 0) {
+ return holder;
+ }
+
+ File[] files = directory.listFiles();
+
+ for (int i = 0; (files != null) && (i < files.length); i++) {
+ String name = files[i].getAbsolutePath();
+
+ if (!(name.startsWith(root))) {
+ continue;
+ } else if (files[i].isDirectory()) {
+ getClassFiles(root, holder, files[i], maxDirectories);
+ } else if (files[i].getName().endsWith(".class")) {
+ holder.add(files[i].getAbsolutePath().
+ substring(root.length() + 1));
+ }
+ }
+
+ return holder;
+ }
+}
diff --git a/lib/src/src/main/java/jline/CompletionHandler.java b/lib/src/src/main/java/jline/CompletionHandler.java
new file mode 100644
index 0000000..6f524da
--- /dev/null
+++ b/lib/src/src/main/java/jline/CompletionHandler.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved.
+ *
+ * This software is distributable under the BSD license. See the terms of the
+ * BSD license in the documentation provided with this software.
+ */
+package jline;
+
+import java.io.*;
+import java.util.*;
+
+/**
+ * Handler for dealing with candidates for tab-completion.
+ *
+ * @author Marc Prud'hommeaux
+ */
+public interface CompletionHandler {
+ boolean complete(ConsoleReader reader, List candidates, int position)
+ throws IOException;
+}
diff --git a/lib/src/src/main/java/jline/Completor.java b/lib/src/src/main/java/jline/Completor.java
new file mode 100644
index 0000000..ad29cf9
--- /dev/null
+++ b/lib/src/src/main/java/jline/Completor.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved.
+ *
+ * This software is distributable under the BSD license. See the terms of the
+ * BSD license in the documentation provided with this software.
+ */
+package jline;
+
+import java.util.*;
+
+/**
+ * A Completor is the mechanism by which tab-completion candidates
+ * will be resolved.
+ *
+ * @author Marc Prud'hommeaux
+ */
+public interface Completor {
+ /**
+ * Populates candidates with a list of possible
+ * completions for the buffer. The candidates
+ * list will not be sorted before being displayed to the
+ * user: thus, the complete method should sort the
+ * {@link List} before returning.
+ *
+ *
+ * @param buffer the buffer
+ * @param candidates the {@link List} of candidates to populate
+ * @return the index of the buffer for which
+ * the completion will be relative
+ */
+ int complete(String buffer, int cursor, List candidates);
+}
diff --git a/lib/src/src/main/java/jline/ConsoleOperations.java b/lib/src/src/main/java/jline/ConsoleOperations.java
new file mode 100644
index 0000000..16aa0e7
--- /dev/null
+++ b/lib/src/src/main/java/jline/ConsoleOperations.java
@@ -0,0 +1,282 @@
+/*
+ * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved.
+ *
+ * This software is distributable under the BSD license. See the terms of the
+ * BSD license in the documentation provided with this software.
+ */
+package jline;
+
+import java.awt.event.KeyEvent;
+
+/**
+ * Symbolic constants for Console operations and virtual key bindings.
+ * @see KeyEvent
+ *
+ * @author Marc Prud'hommeaux
+ */
+public interface ConsoleOperations {
+ final String CR = System.getProperty("line.separator");
+ final char BACKSPACE = '\b';
+ final char RESET_LINE = '\r';
+ final char KEYBOARD_BELL = '\07';
+ final char CTRL_A = 1;
+ final char CTRL_B = 2;
+ final char CTRL_C = 3;
+ final char CTRL_D = 4;
+ final char CTRL_E = 5;
+ final char CTRL_F = 6;
+ final char CTRL_G = 7;
+ final static char CTRL_K = 11;
+ final static char CTRL_L = 12;
+ final char CTRL_N = 14;
+ final char CTRL_P = 16;
+ final static char CTRL_OB = 27;
+ final static char DELETE = 127;
+ final static char CTRL_QM = 127;
+
+
+ /**
+ * Logical constants for key operations.
+ */
+
+ /**
+ * Unknown operation.
+ */
+ final short UNKNOWN = -99;
+
+ /**
+ * Operation that moves to the beginning of the buffer.
+ */
+ final short MOVE_TO_BEG = -1;
+
+ /**
+ * Operation that moves to the end of the buffer.
+ */
+ final short MOVE_TO_END = -3;
+
+ /**
+ * Operation that moved to the previous character in the buffer.
+ */
+ final short PREV_CHAR = -4;
+
+ /**
+ * Operation that issues a newline.
+ */
+ final short NEWLINE = -6;
+
+ /**
+ * Operation that deletes the buffer from the current character to the end.
+ */
+ final short KILL_LINE = -7;
+
+ /**
+ * Operation that clears the screen.
+ */
+ final short CLEAR_SCREEN = -8;
+
+ /**
+ * Operation that sets the buffer to the next history item.
+ */
+ final short NEXT_HISTORY = -9;
+
+ /**
+ * Operation that sets the buffer to the previous history item.
+ */
+ final short PREV_HISTORY = -11;
+
+ /**
+ * Operation that redisplays the current buffer.
+ */
+ final short REDISPLAY = -13;
+
+ /**
+ * Operation that deletes the buffer from the cursor to the beginning.
+ */
+ final short KILL_LINE_PREV = -15;
+
+ /**
+ * Operation that deletes the previous word in the buffer.
+ */
+ final short DELETE_PREV_WORD = -16;
+
+ /**
+ * Operation that moves to the next character in the buffer.
+ */
+ final short NEXT_CHAR = -19;
+
+ /**
+ * Operation that moves to the previous character in the buffer.
+ */
+ final short REPEAT_PREV_CHAR = -20;
+
+ /**
+ * Operation that searches backwards in the command history.
+ */
+ final short SEARCH_PREV = -21;
+
+ /**
+ * Operation that repeats the character.
+ */
+ final short REPEAT_NEXT_CHAR = -24;
+
+ /**
+ * Operation that searches forward in the command history.
+ */
+ final short SEARCH_NEXT = -25;
+
+ /**
+ * Operation that moved to the previous whitespace.
+ */
+ final short PREV_SPACE_WORD = -27;
+
+ /**
+ * Operation that moved to the end of the current word.
+ */
+ final short TO_END_WORD = -29;
+
+ /**
+ * Operation that
+ */
+ final short REPEAT_SEARCH_PREV = -34;
+
+ /**
+ * Operation that
+ */
+ final short PASTE_PREV = -36;
+
+ /**
+ * Operation that
+ */
+ final short REPLACE_MODE = -37;
+
+ /**
+ * Operation that
+ */
+ final short SUBSTITUTE_LINE = -38;
+
+ /**
+ * Operation that
+ */
+ final short TO_PREV_CHAR = -39;
+
+ /**
+ * Operation that
+ */
+ final short NEXT_SPACE_WORD = -40;
+
+ /**
+ * Operation that
+ */
+ final short DELETE_PREV_CHAR = -41;
+
+ /**
+ * Operation that
+ */
+ final short ADD = -42;
+
+ /**
+ * Operation that
+ */
+ final short PREV_WORD = -43;
+
+ /**
+ * Operation that
+ */
+ final short CHANGE_META = -44;
+
+ /**
+ * Operation that
+ */
+ final short DELETE_META = -45;
+
+ /**
+ * Operation that
+ */
+ final short END_WORD = -46;
+
+ /**
+ * Operation that toggles insert/overtype
+ */
+ final short INSERT = -48;
+
+ /**
+ * Operation that
+ */
+ final short REPEAT_SEARCH_NEXT = -49;
+
+ /**
+ * Operation that
+ */
+ final short PASTE_NEXT = -50;
+
+ /**
+ * Operation that
+ */
+ final short REPLACE_CHAR = -51;
+
+ /**
+ * Operation that
+ */
+ final short SUBSTITUTE_CHAR = -52;
+
+ /**
+ * Operation that
+ */
+ final short TO_NEXT_CHAR = -53;
+
+ /**
+ * Operation that undoes the previous operation.
+ */
+ final short UNDO = -54;
+
+ /**
+ * Operation that moved to the next word.
+ */
+ final short NEXT_WORD = -55;
+
+ /**
+ * Operation that deletes the previous character.
+ */
+ final short DELETE_NEXT_CHAR = -56;
+
+ /**
+ * Operation that toggles between uppercase and lowercase.
+ */
+ final short CHANGE_CASE = -57;
+
+ /**
+ * Operation that performs completion operation on the current word.
+ */
+ final short COMPLETE = -58;
+
+ /**
+ * Operation that exits the command prompt.
+ */
+ final short EXIT = -59;
+
+ /**
+ * Operation that pastes the contents of the clipboard into the line
+ */
+ final short PASTE = -60;
+
+ /**
+ * Operation that moves the current History to the beginning.
+ */
+ final static short START_OF_HISTORY = -61;
+
+ /**
+ * Operation that moves the current History to the end.
+ */
+ final static short END_OF_HISTORY = -62;
+
+ /**
+ * Operation that clears whatever text is on the current line.
+ */
+ final static short CLEAR_LINE = -63;
+
+ /**
+ * Operation that aborts the current command (like searching)
+ */
+ final static short ABORT = -64;
+
+}
diff --git a/lib/src/src/main/java/jline/ConsoleReader.java b/lib/src/src/main/java/jline/ConsoleReader.java
new file mode 100644
index 0000000..18339d4
--- /dev/null
+++ b/lib/src/src/main/java/jline/ConsoleReader.java
@@ -0,0 +1,1823 @@
+/*
+ * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved.
+ *
+ * This software is distributable under the BSD license. See the terms of the
+ * BSD license in the documentation provided with this software.
+ */
+package jline;
+
+import java.awt.*;
+import java.awt.datatransfer.*;
+import java.awt.event.ActionListener;
+
+import java.io.*;
+import java.util.*;
+import java.util.List;
+
+/**
+ * A reader for console applications. It supports custom tab-completion,
+ * saveable command history, and command line editing. On some platforms,
+ * platform-specific commands will need to be issued before the reader will
+ * function properly. See {@link Terminal#initializeTerminal} for convenience
+ * methods for issuing platform-specific setup commands.
+ *
+ * @author Marc Prud'hommeaux
+ */
+public class ConsoleReader implements ConsoleOperations {
+
+ final static int TAB_WIDTH = 4;
+ String prompt;
+ private boolean useHistory = true;
+ private boolean usePagination = false;
+ public static final String CR = System.getProperty("line.separator");
+ private static ResourceBundle loc = ResourceBundle.getBundle(CandidateListCompletionHandler.class.getName());
+ /**
+ * Map that contains the operation name to keymay operation mapping.
+ */
+ public static SortedMap KEYMAP_NAMES;
+
+
+ static {
+ Map names = new TreeMap();
+
+ names.put("MOVE_TO_BEG", new Short(MOVE_TO_BEG));
+ names.put("MOVE_TO_END", new Short(MOVE_TO_END));
+ names.put("PREV_CHAR", new Short(PREV_CHAR));
+ names.put("NEWLINE", new Short(NEWLINE));
+ names.put("KILL_LINE", new Short(KILL_LINE));
+ names.put("PASTE", new Short(PASTE));
+ names.put("CLEAR_SCREEN", new Short(CLEAR_SCREEN));
+ names.put("NEXT_HISTORY", new Short(NEXT_HISTORY));
+ names.put("PREV_HISTORY", new Short(PREV_HISTORY));
+ names.put("START_OF_HISTORY", new Short(START_OF_HISTORY));
+ names.put("END_OF_HISTORY", new Short(END_OF_HISTORY));
+ names.put("REDISPLAY", new Short(REDISPLAY));
+ names.put("KILL_LINE_PREV", new Short(KILL_LINE_PREV));
+ names.put("DELETE_PREV_WORD", new Short(DELETE_PREV_WORD));
+ names.put("NEXT_CHAR", new Short(NEXT_CHAR));
+ names.put("REPEAT_PREV_CHAR", new Short(REPEAT_PREV_CHAR));
+ names.put("SEARCH_PREV", new Short(SEARCH_PREV));
+ names.put("REPEAT_NEXT_CHAR", new Short(REPEAT_NEXT_CHAR));
+ names.put("SEARCH_NEXT", new Short(SEARCH_NEXT));
+ names.put("PREV_SPACE_WORD", new Short(PREV_SPACE_WORD));
+ names.put("TO_END_WORD", new Short(TO_END_WORD));
+ names.put("REPEAT_SEARCH_PREV", new Short(REPEAT_SEARCH_PREV));
+ names.put("PASTE_PREV", new Short(PASTE_PREV));
+ names.put("REPLACE_MODE", new Short(REPLACE_MODE));
+ names.put("SUBSTITUTE_LINE", new Short(SUBSTITUTE_LINE));
+ names.put("TO_PREV_CHAR", new Short(TO_PREV_CHAR));
+ names.put("NEXT_SPACE_WORD", new Short(NEXT_SPACE_WORD));
+ names.put("DELETE_PREV_CHAR", new Short(DELETE_PREV_CHAR));
+ names.put("ADD", new Short(ADD));
+ names.put("PREV_WORD", new Short(PREV_WORD));
+ names.put("CHANGE_META", new Short(CHANGE_META));
+ names.put("DELETE_META", new Short(DELETE_META));
+ names.put("END_WORD", new Short(END_WORD));
+ names.put("NEXT_CHAR", new Short(NEXT_CHAR));
+ names.put("INSERT", new Short(INSERT));
+ names.put("REPEAT_SEARCH_NEXT", new Short(REPEAT_SEARCH_NEXT));
+ names.put("PASTE_NEXT", new Short(PASTE_NEXT));
+ names.put("REPLACE_CHAR", new Short(REPLACE_CHAR));
+ names.put("SUBSTITUTE_CHAR", new Short(SUBSTITUTE_CHAR));
+ names.put("TO_NEXT_CHAR", new Short(TO_NEXT_CHAR));
+ names.put("UNDO", new Short(UNDO));
+ names.put("NEXT_WORD", new Short(NEXT_WORD));
+ names.put("DELETE_NEXT_CHAR", new Short(DELETE_NEXT_CHAR));
+ names.put("CHANGE_CASE", new Short(CHANGE_CASE));
+ names.put("COMPLETE", new Short(COMPLETE));
+ names.put("EXIT", new Short(EXIT));
+ names.put("CLEAR_LINE", new Short(CLEAR_LINE));
+ names.put("ABORT", new Short(ABORT));
+
+ KEYMAP_NAMES = new TreeMap(Collections.unmodifiableMap(names));
+ }
+ /**
+ * The map for logical operations.
+ */
+ private final short[] keybindings;
+ /**
+ * If true, issue an audible keyboard bell when appropriate.
+ */
+ private boolean bellEnabled = true;
+ /**
+ * The current character mask.
+ */
+ private Character mask = null;
+ /**
+ * The null mask.
+ */
+ private static final Character NULL_MASK = new Character((char) 0);
+ /**
+ * The number of tab-completion candidates above which a warning will be
+ * prompted before showing all the candidates.
+ */
+ private int autoprintThreshhold = Integer.getInteger(
+ "jline.completion.threshold", 100).intValue(); // same default as
+
+ // bash
+ /**
+ * The Terminal to use.
+ */
+ private final Terminal terminal;
+ private CompletionHandler completionHandler = new CandidateListCompletionHandler();
+ InputStream in;
+ final Writer out;
+ final CursorBuffer buf = new CursorBuffer();
+ static PrintWriter debugger;
+ History history = new History();
+ final List completors = new LinkedList();
+ private Character echoCharacter = null;
+ private Map triggeredActions = new HashMap();
+
+ private StringBuffer searchTerm = null;
+ private String previousSearchTerm = "";
+ private int searchIndex = -1;
+
+ /**
+ * Adding a triggered Action allows to give another course of action
+ * if a character passed the preprocessing.
+ *
+ * Say you want to close the application if the user enter q.
+ * addTriggerAction('q', new ActionListener(){ System.exit(0); });
+ * would do the trick.
+ *
+ * @param c
+ * @param listener
+ */
+ public void addTriggeredAction(char c, ActionListener listener) {
+ triggeredActions.put(new Character(c), listener);
+ }
+
+ /**
+ * Create a new reader using {@link FileDescriptor#in} for input and
+ * {@link System#out} for output. {@link FileDescriptor#in} is used because
+ * it has a better chance of being unbuffered.
+ */
+ public ConsoleReader() throws IOException {
+ this(new FileInputStream(FileDescriptor.in),
+ new PrintWriter(
+ new OutputStreamWriter(System.out,
+ System.getProperty("jline.WindowsTerminal.output.encoding", System.getProperty("file.encoding")))));
+ }
+
+ /**
+ * Create a new reader using the specified {@link InputStream} for input and
+ * the specific writer for output, using the default keybindings resource.
+ */
+ public ConsoleReader(final InputStream in, final Writer out)
+ throws IOException {
+ this(in, out, null);
+ }
+
+ public ConsoleReader(final InputStream in, final Writer out,
+ final InputStream bindings) throws IOException {
+ this(in, out, bindings, Terminal.getTerminal());
+ }
+
+ /**
+ * Create a new reader.
+ *
+ * @param in
+ * the input
+ * @param out
+ * the output
+ * @param bindings
+ * the key bindings to use
+ * @param term
+ * the terminal to use
+ */
+ public ConsoleReader(InputStream in, Writer out, InputStream bindings,
+ Terminal term) throws IOException {
+ this.terminal = term;
+ setInput(in);
+ this.out = out;
+ if (bindings == null) {
+ try {
+ String bindingFile = System.getProperty("jline.keybindings",
+ new File(System.getProperty("user.home"),
+ ".jlinebindings.properties").getAbsolutePath());
+
+ if (new File(bindingFile).isFile()) {
+ bindings = new FileInputStream(new File(bindingFile));
+ }
+ } catch (Exception e) {
+ // swallow exceptions with option debugging
+ if (debugger != null) {
+ e.printStackTrace(debugger);
+ }
+ }
+ }
+
+ if (bindings == null) {
+ bindings = terminal.getDefaultBindings();
+ }
+
+ this.keybindings = new short[Character.MAX_VALUE * 2];
+
+ Arrays.fill(this.keybindings, UNKNOWN);
+
+ /**
+ * Loads the key bindings. Bindings file is in the format:
+ *
+ * keycode: operation name
+ */
+ if (bindings != null) {
+ Properties p = new Properties();
+ p.load(bindings);
+ bindings.close();
+
+ for (Iterator i = p.keySet().iterator(); i.hasNext();) {
+ String val = (String) i.next();
+
+ try {
+ Short code = new Short(val);
+ String op = (String) p.getProperty(val);
+
+ Short opval = (Short) KEYMAP_NAMES.get(op);
+
+ if (opval != null) {
+ keybindings[code.shortValue()] = opval.shortValue();
+ }
+ } catch (NumberFormatException nfe) {
+ consumeException(nfe);
+ }
+ }
+
+ // hardwired arrow key bindings
+ // keybindings[VK_UP] = PREV_HISTORY;
+ // keybindings[VK_DOWN] = NEXT_HISTORY;
+ // keybindings[VK_LEFT] = PREV_CHAR;
+ // keybindings[VK_RIGHT] = NEXT_CHAR;
+ }
+ }
+
+ public Terminal getTerminal() {
+ return this.terminal;
+ }
+
+ /**
+ * Set the stream for debugging. Development use only.
+ */
+ public void setDebug(final PrintWriter debugger) {
+ ConsoleReader.debugger = debugger;
+ }
+
+ /**
+ * Set the stream to be used for console input.
+ */
+ public void setInput(final InputStream in) {
+ this.in = in;
+ }
+
+ /**
+ * Returns the stream used for console input.
+ */
+ public InputStream getInput() {
+ return this.in;
+ }
+
+ /**
+ * Read the next line and return the contents of the buffer.
+ */
+ public String readLine() throws IOException {
+ return readLine((String) null);
+ }
+
+ /**
+ * Read the next line with the specified character mask. If null, then
+ * characters will be echoed. If 0, then no characters will be echoed.
+ */
+ public String readLine(final Character mask) throws IOException {
+ return readLine(null, mask);
+ }
+
+ /**
+ * @param bellEnabled
+ * if true, enable audible keyboard bells if an alert is
+ * required.
+ */
+ public void setBellEnabled(final boolean bellEnabled) {
+ this.bellEnabled = bellEnabled;
+ }
+
+ /**
+ * @return true is audible keyboard bell is enabled.
+ */
+ public boolean getBellEnabled() {
+ return this.bellEnabled;
+ }
+
+ /**
+ * Query the terminal to find the current width;
+ *
+ * @see Terminal#getTerminalWidth
+ * @return the width of the current terminal.
+ */
+ public int getTermwidth() {
+ return getTerminal().getTerminalWidth();
+ }
+
+ /**
+ * Query the terminal to find the current width;
+ *
+ * @see Terminal#getTerminalHeight
+ *
+ * @return the height of the current terminal.
+ */
+ public int getTermheight() {
+ return getTerminal().getTerminalHeight();
+ }
+
+ /**
+ * @param autoprintThreshhold
+ * the number of candidates to print without issuing a warning.
+ */
+ public void setAutoprintThreshhold(final int autoprintThreshhold) {
+ this.autoprintThreshhold = autoprintThreshhold;
+ }
+
+ /**
+ * @return the number of candidates to print without issing a warning.
+ */
+ public int getAutoprintThreshhold() {
+ return this.autoprintThreshhold;
+ }
+
+ int getKeyForAction(short logicalAction) {
+ for (int i = 0; i < keybindings.length; i++) {
+ if (keybindings[i] == logicalAction) {
+ return i;
+ }
+ }
+
+ return -1;
+ }
+
+ /**
+ * Clear the echoed characters for the specified character code.
+ */
+ int clearEcho(int c) throws IOException {
+ // if the terminal is not echoing, then just return...
+ if (!terminal.getEcho()) {
+ return 0;
+ }
+
+ // otherwise, clear
+ int num = countEchoCharacters((char) c);
+ back(num);
+ drawBuffer(num);
+
+ return num;
+ }
+
+ int countEchoCharacters(char c) {
+ // tabs as special: we need to determine the number of spaces
+ // to cancel based on what out current cursor position is
+ if (c == 9) {
+ int tabstop = 8; // will this ever be different?
+ int position = getCursorPosition();
+
+ return tabstop - (position % tabstop);
+ }
+
+ return getPrintableCharacters(c).length();
+ }
+
+ /**
+ * Return the number of characters that will be printed when the specified
+ * character is echoed to the screen. Adapted from cat by Torbjorn Granlund,
+ * as repeated in stty by David MacKenzie.
+ */
+ StringBuffer getPrintableCharacters(char ch) {
+ StringBuffer sbuff = new StringBuffer();
+
+ if (ch >= 32) {
+ if (ch < 127) {
+ sbuff.append(ch);
+ } else if (ch == 127) {
+ sbuff.append('^');
+ sbuff.append('?');
+ } else {
+ sbuff.append('M');
+ sbuff.append('-');
+
+ if (ch >= (128 + 32)) {
+ if (ch < (128 + 127)) {
+ sbuff.append((char) (ch - 128));
+ } else {
+ sbuff.append('^');
+ sbuff.append('?');
+ }
+ } else {
+ sbuff.append('^');
+ sbuff.append((char) (ch - 128 + 64));
+ }
+ }
+ } else {
+ sbuff.append('^');
+ sbuff.append((char) (ch + 64));
+ }
+
+ return sbuff;
+ }
+
+ int getCursorPosition() {
+ // FIXME: does not handle anything but a line with a prompt
+ // absolute position
+ return getStrippedAnsiLength(prompt) + buf.cursor;
+ }
+
+ /**
+ * Strips ANSI escape sequences starting with CSI and ending with char in range 64-126
+ * @param ansiString String possibly containing ANSI codes, may be null
+ * @return length after stripping ANSI codes
+ */
+ int getStrippedAnsiLength(String ansiString) {
+ if (ansiString == null) return 0;
+ boolean inAnsi = false;
+ int strippedLength = 0;
+ char[] chars = ansiString.toCharArray();
+ for (int i = 0; i < chars.length; i++) {
+ char c = chars[i];
+ if (!inAnsi && c == 27 && i < chars.length - 1 && chars[i+1] == '[') {
+ i++; // skip '['
+ inAnsi = true;
+ } else if (inAnsi) {
+ if (64 <= c && c <= 126) {
+ inAnsi = false;
+ }
+ } else {
+ strippedLength++;
+ }
+ }
+ return strippedLength;
+ }
+
+ public String readLine(final String prompt) throws IOException {
+ return readLine(prompt, null);
+ }
+
+ /**
+ * The default prompt that will be issued.
+ */
+ public void setDefaultPrompt(String prompt) {
+ this.prompt = prompt;
+ }
+
+ /**
+ * The default prompt that will be issued.
+ */
+ public String getDefaultPrompt() {
+ return prompt;
+ }
+
+ /**
+ * Read a line from the in {@link InputStream}, and return the line
+ * (without any trailing newlines).
+ *
+ * @param prompt
+ * the prompt to issue to the console, may be null.
+ * @return a line that is read from the terminal, or null if there was null
+ * input (e.g., CTRL-D was pressed).
+ */
+ public String readLine(final String prompt, final Character mask)
+ throws IOException {
+ this.mask = mask;
+ if (prompt != null) {
+ this.prompt = prompt;
+ }
+
+ try {
+ terminal.beforeReadLine(this, this.prompt, mask);
+
+ if ((this.prompt != null) && (this.prompt.length() > 0)) {
+ out.write(this.prompt);
+ out.flush();
+ }
+
+ // if the terminal is unsupported, just use plain-java reading
+ if (!terminal.isSupported()) {
+ return readLine(in);
+ }
+
+ final int NORMAL = 1;
+ final int SEARCH = 2;
+ int state = NORMAL;
+
+ boolean success = true;
+
+ while (true) {
+ // Read next key and look up the command binding.
+ int[] next = readBinding();
+
+ if (next == null) {
+ return null;
+ }
+
+ int c = next[0];
+ int code = next[1];
+
+ if (c == -1) {
+ return null;
+ }
+
+ // Search mode.
+ //
+ // Note that we have to do this first, because if there is a command
+ // not linked to a search command, we leave the search mode and fall
+ // through to the normal state.
+ if (state == SEARCH) {
+ switch (code) {
+ // This doesn't work right now, it seems CTRL-G is not passed
+ // down correctly. :(
+ case ABORT:
+ state = NORMAL;
+ break;
+
+ case SEARCH_PREV:
+ if (searchTerm.length() == 0) {
+ searchTerm.append(previousSearchTerm);
+ }
+
+ if (searchIndex == -1) {
+ searchIndex = history.searchBackwards(searchTerm.toString());
+ } else {
+ searchIndex = history.searchBackwards(searchTerm.toString(), searchIndex);
+ }
+ break;
+
+ case DELETE_PREV_CHAR:
+ if (searchTerm.length() > 0) {
+ searchTerm.deleteCharAt(searchTerm.length() - 1);
+ searchIndex = history.searchBackwards(searchTerm.toString());
+ }
+ break;
+
+ case UNKNOWN:
+ searchTerm.appendCodePoint(c);
+ searchIndex = history.searchBackwards(searchTerm.toString());
+ break;
+
+ default:
+ // Set buffer and cursor position to the found string.
+ if (searchIndex != -1) {
+ history.setCurrentIndex(searchIndex);
+ setBuffer(history.current());
+ buf.cursor = history.current().indexOf(searchTerm.toString());
+ }
+ state = NORMAL;
+ break;
+ }
+
+ // if we're still in search mode, print the search status
+ if (state == SEARCH) {
+ if (searchTerm.length() == 0) {
+ printSearchStatus("", "");
+ } else {
+ if (searchIndex == -1) {
+ beep();
+ } else {
+ printSearchStatus(searchTerm.toString(), history.getHistory(searchIndex));
+ }
+ }
+ }
+ // otherwise, restore the line
+ else {
+ restoreLine();
+ }
+ }
+
+ if (state == NORMAL) {
+ switch (code) {
+ case EXIT: // ctrl-d
+
+ if (buf.buffer.length() == 0) {
+ return null;
+ }
+ else {
+ success = deleteCurrentCharacter();
+ }
+ break;
+
+ case COMPLETE: // tab
+ success = complete();
+ break;
+
+ case MOVE_TO_BEG:
+ success = setCursorPosition(0);
+ break;
+
+ case KILL_LINE: // CTRL-K
+ success = killLine();
+ break;
+
+ case CLEAR_SCREEN: // CTRL-L
+ success = clearScreen();
+ break;
+
+ case KILL_LINE_PREV: // CTRL-U
+ success = resetLine();
+ break;
+
+ case NEWLINE: // enter
+ moveToEnd();
+ printNewline(); // output newline
+ return finishBuffer();
+
+ case DELETE_PREV_CHAR: // backspace
+ success = backspace();
+ break;
+
+ case DELETE_NEXT_CHAR: // delete
+ success = deleteCurrentCharacter();
+ break;
+
+ case MOVE_TO_END:
+ success = moveToEnd();
+ break;
+
+ case PREV_CHAR:
+ success = moveCursor(-1) != 0;
+ break;
+
+ case NEXT_CHAR:
+ success = moveCursor(1) != 0;
+ break;
+
+ case NEXT_HISTORY:
+ success = moveHistory(true);
+ break;
+
+ case PREV_HISTORY:
+ success = moveHistory(false);
+ break;
+
+ case ABORT:
+ case REDISPLAY:
+ break;
+
+ case PASTE:
+ success = paste();
+ break;
+
+ case DELETE_PREV_WORD:
+ success = deletePreviousWord();
+ break;
+
+ case PREV_WORD:
+ success = previousWord();
+ break;
+
+ case NEXT_WORD:
+ success = nextWord();
+ break;
+
+ case START_OF_HISTORY:
+ success = history.moveToFirstEntry();
+ if (success) {
+ setBuffer(history.current());
+ }
+ break;
+
+ case END_OF_HISTORY:
+ success = history.moveToLastEntry();
+ if (success) {
+ setBuffer(history.current());
+ }
+ break;
+
+ case CLEAR_LINE:
+ moveInternal(-(buf.buffer.length()));
+ killLine();
+ break;
+
+ case INSERT:
+ buf.setOvertyping(!buf.isOvertyping());
+ break;
+
+ case SEARCH_PREV: // CTRL-R
+ if (searchTerm != null) {
+ previousSearchTerm = searchTerm.toString();
+ }
+ searchTerm = new StringBuffer(buf.buffer);
+ state = SEARCH;
+ if (searchTerm.length() > 0) {
+ searchIndex = history.searchBackwards(searchTerm.toString());
+ if (searchIndex == -1) {
+ beep();
+ }
+ printSearchStatus(searchTerm.toString(),
+ searchIndex > -1 ? history.getHistory(searchIndex) : "");
+ } else {
+ searchIndex = -1;
+ printSearchStatus("", "");
+ }
+ break;
+
+ case UNKNOWN:
+ default:
+ if (c != 0) { // ignore null chars
+ ActionListener action = (ActionListener) triggeredActions.get(new Character((char) c));
+ if (action != null) {
+ action.actionPerformed(null);
+ } else {
+ putChar(c, true);
+ }
+ } else {
+ success = false;
+ }
+ }
+
+ if (!(success)) {
+ beep();
+ }
+
+ flushConsole();
+ }
+ }
+ } finally {
+ terminal.afterReadLine(this, this.prompt, mask);
+ }
+ }
+
+ private String readLine(InputStream in) throws IOException {
+ StringBuffer buf = new StringBuffer();
+
+ while (true) {
+ int i = in.read();
+
+ if ((i == -1) || (i == '\n') || (i == '\r')) {
+ return buf.toString();
+ }
+
+ buf.append((char) i);
+ }
+
+ // return new BufferedReader (new InputStreamReader (in)).readLine ();
+ }
+
+ /**
+ * Reads the console input and returns an array of the form [raw, key
+ * binding].
+ */
+ private int[] readBinding() throws IOException {
+ int c = readVirtualKey();
+
+ if (c == -1) {
+ return null;
+ }
+
+ // extract the appropriate key binding
+ short code = keybindings[c];
+
+ if (debugger != null) {
+ // debug(" translated: " + (int) c + ": " + code);
+ }
+
+ return new int[]{c, code};
+ }
+
+ /**
+ * Move up or down the history tree.
+ */
+ private final boolean moveHistory(final boolean next) throws IOException {
+ if (next && !history.next()) {
+ return false;
+ } else if (!next && !history.previous()) {
+ return false;
+ }
+
+ setBuffer(history.current());
+
+ return true;
+ }
+
+ /**
+ * Paste the contents of the clipboard into the console buffer
+ *
+ * @return true if clipboard contents pasted
+ */
+ public boolean paste() throws IOException {
+ Clipboard clipboard;
+ try { // May throw ugly exception on system without X
+ clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
+ } catch (Exception e) {
+ return false;
+ }
+
+ if (clipboard == null) {
+ return false;
+ }
+
+ Transferable transferable = clipboard.getContents(null);
+
+ if (transferable == null) {
+ return false;
+ }
+
+ try {
+ Object content = transferable.getTransferData(DataFlavor.plainTextFlavor);
+
+ /*
+ * This fix was suggested in bug #1060649 at
+ * http://sourceforge.net/tracker/index.php?func=detail&aid=1060649&group_id=64033&atid=506056
+ * to get around the deprecated DataFlavor.plainTextFlavor, but it
+ * raises a UnsupportedFlavorException on Mac OS X
+ */
+ if (content == null) {
+ try {
+ content = new DataFlavor().getReaderForText(transferable);
+ } catch (Exception e) {
+ }
+ }
+
+ if (content == null) {
+ return false;
+ }
+
+ String value;
+
+ if (content instanceof Reader) {
+ // TODO: we might want instead connect to the input stream
+ // so we can interpret individual lines
+ value = "";
+
+ String line = null;
+
+ for (BufferedReader read = new BufferedReader((Reader) content); (line = read.readLine()) != null;) {
+ if (value.length() > 0) {
+ value += "\n";
+ }
+
+ value += line;
+ }
+ } else {
+ value = content.toString();
+ }
+
+ if (value == null) {
+ return true;
+ }
+
+ putString(value);
+
+ return true;
+ } catch (UnsupportedFlavorException ufe) {
+ if (debugger != null) {
+ debug(ufe + "");
+ }
+
+ return false;
+ }
+ }
+
+ /**
+ * Kill the buffer ahead of the current cursor position.
+ *
+ * @return true if successful
+ */
+ public boolean killLine() throws IOException {
+ int cp = buf.cursor;
+ int len = buf.buffer.length();
+
+ if (cp >= len) {
+ return false;
+ }
+
+ int num = buf.buffer.length() - cp;
+ clearAhead(num);
+
+ for (int i = 0; i < num; i++) {
+ buf.buffer.deleteCharAt(len - i - 1);
+ }
+
+ return true;
+ }
+
+ /**
+ * Clear the screen by issuing the ANSI "clear screen" code.
+ */
+ public boolean clearScreen() throws IOException {
+ if (!terminal.isANSISupported()) {
+ return false;
+ }
+
+ // send the ANSI code to clear the screen
+ printANSISequence("2J");
+
+ // then send the ANSI code to go to position 1,1
+ printANSISequence("1;1H");
+
+ redrawLine();
+
+ return true;
+ }
+
+ /**
+ * Use the completors to modify the buffer with the appropriate completions.
+ *
+ * @return true if successful
+ */
+ private final boolean complete() throws IOException {
+ // debug ("tab for (" + buf + ")");
+ if (completors.size() == 0) {
+ return false;
+ }
+
+ List candidates = new LinkedList();
+ String bufstr = buf.buffer.toString();
+ int cursor = buf.cursor;
+
+ int position = -1;
+
+ for (Iterator i = completors.iterator(); i.hasNext();) {
+ Completor comp = (Completor) i.next();
+
+ if ((position = comp.complete(bufstr, cursor, candidates)) != -1) {
+ break;
+ }
+ }
+
+ // no candidates? Fail.
+ if (candidates.size() == 0) {
+ return false;
+ }
+
+ return completionHandler.complete(this, candidates, position);
+ }
+
+ public CursorBuffer getCursorBuffer() {
+ return buf;
+ }
+
+ /**
+ * Output the specified {@link Collection} in proper columns.
+ *
+ * @param stuff
+ * the stuff to print
+ */
+ public void printColumns(final Collection stuff) throws IOException {
+ if ((stuff == null) || (stuff.size() == 0)) {
+ return;
+ }
+
+ int width = getTermwidth();
+ int maxwidth = 0;
+
+ for (Iterator i = stuff.iterator(); i.hasNext(); maxwidth = Math.max(
+ maxwidth, i.next().toString().length())) {
+ ;
+ }
+
+ StringBuffer line = new StringBuffer();
+
+ int showLines;
+
+ if (usePagination) {
+ showLines = getTermheight() - 1; // page limit
+ } else {
+ showLines = Integer.MAX_VALUE;
+ }
+
+ for (Iterator i = stuff.iterator(); i.hasNext();) {
+ String cur = (String) i.next();
+
+ if ((line.length() + maxwidth) > width) {
+ printString(line.toString().trim());
+ printNewline();
+ line.setLength(0);
+ if (--showLines == 0) { // Overflow
+ printString(loc.getString("display-more"));
+ flushConsole();
+ int c = readVirtualKey();
+ if (c == '\r' || c == '\n') {
+ showLines = 1; // one step forward
+ } else if (c != 'q') {
+ showLines = getTermheight() - 1; // page forward
+ }
+ back(loc.getString("display-more").length());
+ if (c == 'q') {
+ break; // cancel
+ }
+ }
+ }
+
+ pad(cur, maxwidth + 3, line);
+ }
+
+ if (line.length() > 0) {
+ printString(line.toString().trim());
+ printNewline();
+ line.setLength(0);
+ }
+ }
+
+ /**
+ * Append toPad to the specified appendTo, as well as (toPad.length () -
+ * len) spaces.
+ *
+ * @param toPad
+ * the {@link String} to pad
+ * @param len
+ * the target length
+ * @param appendTo
+ * the {@link StringBuffer} to which to append the padded
+ * {@link String}.
+ */
+ private final void pad(final String toPad, final int len,
+ final StringBuffer appendTo) {
+ appendTo.append(toPad);
+
+ for (int i = 0; i < (len - toPad.length()); i++, appendTo.append(' ')) {
+ ;
+ }
+ }
+
+ /**
+ * Add the specified {@link Completor} to the list of handlers for
+ * tab-completion.
+ *
+ * @param completor
+ * the {@link Completor} to add
+ * @return true if it was successfully added
+ */
+ public boolean addCompletor(final Completor completor) {
+ return completors.add(completor);
+ }
+
+ /**
+ * Remove the specified {@link Completor} from the list of handlers for
+ * tab-completion.
+ *
+ * @param completor
+ * the {@link Completor} to remove
+ * @return true if it was successfully removed
+ */
+ public boolean removeCompletor(final Completor completor) {
+ return completors.remove(completor);
+ }
+
+ /**
+ * Returns an unmodifiable list of all the completors.
+ */
+ public Collection getCompletors() {
+ return Collections.unmodifiableList(completors);
+ }
+
+ /**
+ * Erase the current line.
+ *
+ * @return false if we failed (e.g., the buffer was empty)
+ */
+ final boolean resetLine() throws IOException {
+ if (buf.cursor == 0) {
+ return false;
+ }
+
+ backspaceAll();
+
+ return true;
+ }
+
+ /**
+ * Move the cursor position to the specified absolute index.
+ */
+ public final boolean setCursorPosition(final int position)
+ throws IOException {
+ return moveCursor(position - buf.cursor) != 0;
+ }
+
+ /**
+ * Set the current buffer's content to the specified {@link String}. The
+ * visual console will be modified to show the current buffer.
+ *
+ * @param buffer
+ * the new contents of the buffer.
+ */
+ private final void setBuffer(final String buffer) throws IOException {
+ // don't bother modifying it if it is unchanged
+ if (buffer.equals(buf.buffer.toString())) {
+ return;
+ }
+
+ // obtain the difference between the current buffer and the new one
+ int sameIndex = 0;
+
+ for (int i = 0, l1 = buffer.length(), l2 = buf.buffer.length(); (i < l1) && (i < l2); i++) {
+ if (buffer.charAt(i) == buf.buffer.charAt(i)) {
+ sameIndex++;
+ } else {
+ break;
+ }
+ }
+
+ int diff = buf.cursor - sameIndex;
+ if (diff < 0) { // we can't backspace here so try from the end of the buffer
+ moveToEnd();
+ diff = buf.buffer.length() - sameIndex;
+ }
+
+ backspace(diff); // go back for the differences
+ killLine(); // clear to the end of the line
+ buf.buffer.setLength(sameIndex); // the new length
+ putString(buffer.substring(sameIndex)); // append the differences
+ }
+
+ /**
+ * Clear the line and redraw it.
+ */
+ public final void redrawLine() throws IOException {
+ printCharacter(RESET_LINE);
+ flushConsole();
+ drawLine();
+ }
+
+ /**
+ * Output put the prompt + the current buffer
+ */
+ public final void drawLine() throws IOException {
+ if (prompt != null) {
+ printString(prompt);
+ }
+
+ printString(buf.buffer.toString());
+
+ if (buf.length() != buf.cursor) // not at end of line
+ {
+ back(buf.length() - buf.cursor - 1); // sync
+ }
+ }
+
+ /**
+ * Output a platform-dependant newline.
+ */
+ public final void printNewline() throws IOException {
+ printString(CR);
+ flushConsole();
+ }
+
+ /**
+ * Clear the buffer and add its contents to the history.
+ *
+ * @return the former contents of the buffer.
+ */
+ final String finishBuffer() {
+ String str = buf.buffer.toString();
+
+ // we only add it to the history if the buffer is not empty
+ // and if mask is null, since having a mask typically means
+ // the string was a password. We clear the mask after this call
+ if (str.length() > 0) {
+ if (mask == null && useHistory) {
+ history.addToHistory(str);
+ } else {
+ mask = null;
+ }
+ }
+
+ history.moveToEnd();
+
+ buf.buffer.setLength(0);
+ buf.cursor = 0;
+
+ return str;
+ }
+
+ /**
+ * Write out the specified string to the buffer and the output stream.
+ */
+ public final void putString(final String str) throws IOException {
+ buf.write(str);
+ printString(str);
+ drawBuffer();
+ }
+
+ /**
+ * Output the specified string to the output stream (but not the buffer).
+ */
+ public final void printString(final String str) throws IOException {
+ printCharacters(str.toCharArray());
+ }
+
+ /**
+ * Output the specified character, both to the buffer and the output stream.
+ */
+ private final void putChar(final int c, final boolean print)
+ throws IOException {
+ buf.write((char) c);
+
+ if (print) {
+ // no masking...
+ if (mask == null) {
+ printCharacter(c);
+ } // null mask: don't print anything...
+ else if (mask.charValue() == 0) {
+ ;
+ } // otherwise print the mask...
+ else {
+ printCharacter(mask.charValue());
+ }
+
+ drawBuffer();
+ }
+ }
+
+ /**
+ * Redraw the rest of the buffer from the cursor onwards. This is necessary
+ * for inserting text into the buffer.
+ *
+ * @param clear
+ * the number of characters to clear after the end of the buffer
+ */
+ private final void drawBuffer(final int clear) throws IOException {
+ // debug ("drawBuffer: " + clear);
+ if (buf.cursor == buf.length() && clear == 0) {
+ return;
+ }
+ char[] chars = buf.buffer.substring(buf.cursor).toCharArray();
+ if (mask != null) {
+ Arrays.fill(chars, mask.charValue());
+ }
+
+ printCharacters(chars);
+ clearAhead(clear);
+ if (terminal.isANSISupported()) {
+ if (chars.length > 0) {
+ // don't ask, it seems to work
+ back(Math.max(chars.length - 1, 1));
+ }
+ } else {
+ back(chars.length);
+ }
+ flushConsole();
+ }
+
+ /**
+ * Redraw the rest of the buffer from the cursor onwards. This is necessary
+ * for inserting text into the buffer.
+ */
+ private final void drawBuffer() throws IOException {
+ drawBuffer(0);
+ }
+
+ /**
+ * Clear ahead the specified number of characters without moving the cursor.
+ */
+ private final void clearAhead(final int num) throws IOException {
+ if (num == 0) {
+ return;
+ }
+
+ if (terminal.isANSISupported()) {
+ printANSISequence("J");
+ return;
+ }
+
+ // debug ("clearAhead: " + num);
+
+ // print blank extra characters
+ printCharacters(' ', num);
+
+ // we need to flush here so a "clever" console
+ // doesn't just ignore the redundancy of a space followed by
+ // a backspace.
+ flushConsole();
+
+ // reset the visual cursor
+ back(num);
+
+ flushConsole();
+ }
+
+ /**
+ * Move the visual cursor backwards without modifying the buffer cursor.
+ */
+ private final void back(final int num) throws IOException {
+ if (num == 0) return;
+ if (terminal.isANSISupported()) {
+ int width = getTermwidth();
+ int cursor = getCursorPosition();
+ // debug("back: " + cursor + " + " + num + " on " + width);
+ int currRow = (cursor + num) / width;
+ int newRow = cursor / width;
+ int newCol = cursor % width + 1;
+ // debug(" old row: " + currRow + " new row: " + newRow);
+ if (newRow < currRow) {
+ printANSISequence((currRow - newRow) + "A");
+ }
+ printANSISequence(newCol + "G");
+ flushConsole();
+ return;
+ }
+ printCharacters(BACKSPACE, num);
+ flushConsole();
+ }
+
+ /**
+ * Issue an audible keyboard bell, if {@link #getBellEnabled} return true.
+ */
+ public final void beep() throws IOException {
+ if (!(getBellEnabled())) {
+ return;
+ }
+
+ printCharacter(KEYBOARD_BELL);
+ // need to flush so the console actually beeps
+ flushConsole();
+ }
+
+ /**
+ * Output the specified character to the output stream without manipulating
+ * the current buffer.
+ */
+ private final void printCharacter(final int c) throws IOException {
+ if (c == '\t') {
+ char cbuf[] = new char[TAB_WIDTH];
+ Arrays.fill(cbuf, ' ');
+ out.write(cbuf);
+ return;
+ }
+
+ out.write(c);
+ }
+
+ /**
+ * Output the specified characters to the output stream without manipulating
+ * the current buffer.
+ */
+ private final void printCharacters(final char[] c) throws IOException {
+ int len = 0;
+ for (int i = 0; i < c.length; i++) {
+ if (c[i] == '\t') {
+ len += TAB_WIDTH;
+ } else {
+ len++;
+ }
+ }
+
+ char cbuf[];
+ if (len == c.length) {
+ cbuf = c;
+ } else {
+ cbuf = new char[len];
+ int pos = 0;
+ for (int i = 0; i < c.length; i++) {
+ if (c[i] == '\t') {
+ Arrays.fill(cbuf, pos, pos + TAB_WIDTH, ' ');
+ pos += TAB_WIDTH;
+ } else {
+ cbuf[pos] = c[i];
+ pos++;
+ }
+ }
+ }
+
+ out.write(cbuf);
+ }
+
+ private final void printCharacters(final char c, final int num)
+ throws IOException {
+ if (num == 1) {
+ printCharacter(c);
+ } else {
+ char[] chars = new char[num];
+ Arrays.fill(chars, c);
+ printCharacters(chars);
+ }
+ }
+
+ /**
+ * Flush the console output stream. This is important for printout out
+ * single characters (like a backspace or keyboard) that we want the console
+ * to handle immedately.
+ */
+ public final void flushConsole() throws IOException {
+ out.flush();
+ }
+
+ private final int backspaceAll() throws IOException {
+ return backspace(Integer.MAX_VALUE);
+ }
+
+ /**
+ * Issue num backspaces.
+ *
+ * @return the number of characters backed up
+ */
+ private final int backspace(final int num) throws IOException {
+ if (buf.cursor == 0) {
+ return 0;
+ }
+
+ int count = 0;
+ int termwidth = getTermwidth();
+ int lines = getCursorPosition() / termwidth;
+ count = moveCursor(-1 * num) * -1;
+ // debug ("Deleting from " + buf.cursor + " for " + count);
+ buf.buffer.delete(buf.cursor, buf.cursor + count);
+ if (getCursorPosition() / termwidth != lines) {
+ if (terminal.isANSISupported()) {
+ // debug("doing backspace redraw: " + getCursorPosition() + " on " + termwidth + ": " + lines);
+ printANSISequence("J");
+ flushConsole();
+ }
+ }
+ drawBuffer(count);
+
+ return count;
+ }
+
+ /**
+ * Issue a backspace.
+ *
+ * @return true if successful
+ */
+ public final boolean backspace() throws IOException {
+ return backspace(1) == 1;
+ }
+
+ private final boolean moveToEnd() throws IOException {
+ return moveCursor(buf.length() - buf.cursor) > 0;
+ }
+
+ /**
+ * Delete the character at the current position and redraw the remainder of
+ * the buffer.
+ */
+ private final boolean deleteCurrentCharacter() throws IOException {
+ if (buf.length() == 0 || buf.cursor == buf.length()) {
+ return false;
+ }
+
+ buf.buffer.deleteCharAt(buf.cursor);
+ drawBuffer(1);
+ return true;
+ }
+
+ private final boolean previousWord() throws IOException {
+ while (isDelimiter(buf.current()) && (moveCursor(-1) != 0)) {
+ ;
+ }
+
+ while (!isDelimiter(buf.current()) && (moveCursor(-1) != 0)) {
+ ;
+ }
+
+ return true;
+ }
+
+ private final boolean nextWord() throws IOException {
+ while (isDelimiter(buf.current()) && (moveCursor(1) != 0)) {
+ ;
+ }
+
+ while (!isDelimiter(buf.current()) && (moveCursor(1) != 0)) {
+ ;
+ }
+
+ return true;
+ }
+
+ private final boolean deletePreviousWord() throws IOException {
+ while (isDelimiter(buf.current()) && backspace()) {
+ ;
+ }
+
+ while (!isDelimiter(buf.current()) && backspace()) {
+ ;
+ }
+
+ return true;
+ }
+
+ /**
+ * Move the cursor where characters.
+ *
+ * @param num
+ * if less than 0, move abs(num) to the left,
+ * otherwise move num to the right.
+ *
+ * @return the number of spaces we moved
+ */
+ public final int moveCursor(final int num) throws IOException {
+ int where = num;
+
+ if ((buf.cursor == 0) && (where <= 0)) {
+ return 0;
+ }
+
+ if ((buf.cursor == buf.buffer.length()) && (where >= 0)) {
+ return 0;
+ }
+
+ if ((buf.cursor + where) < 0) {
+ where = -buf.cursor;
+ } else if ((buf.cursor + where) > buf.buffer.length()) {
+ where = buf.buffer.length() - buf.cursor;
+ }
+
+ moveInternal(where);
+
+ return where;
+ }
+
+ /**
+ * debug.
+ *
+ * @param str
+ * the message to issue.
+ */
+ public static void debug(final String str) {
+ if (debugger != null) {
+ debugger.println(str);
+ debugger.flush();
+ }
+ }
+
+ /**
+ * Move the cursor where characters, withough checking the current
+ * buffer.
+ *
+ * @param where
+ * the number of characters to move to the right or left.
+ */
+ private final void moveInternal(final int where) throws IOException {
+ // debug ("move cursor " + where + " ("
+ // + buf.cursor + " => " + (buf.cursor + where) + ")");
+ buf.cursor += where;
+
+ if (terminal.isANSISupported()) {
+ if (where < 0) {
+ back(Math.abs(where));
+ } else {
+ int width = getTermwidth();
+ int cursor = getCursorPosition();
+ int oldLine = (cursor - where) / width;
+ int newLine = cursor / width;
+ if (newLine > oldLine) {
+ printANSISequence((newLine - oldLine) + "B");
+ }
+ printANSISequence(1 +(cursor % width) + "G");
+ }
+ flushConsole();
+ return;
+ }
+
+ char c;
+
+ if (where < 0) {
+ int len = 0;
+ for (int i = buf.cursor; i < buf.cursor - where; i++) {
+ if (buf.getBuffer().charAt(i) == '\t') {
+ len += TAB_WIDTH;
+ } else {
+ len++;
+ }
+ }
+
+ char cbuf[] = new char[len];
+ Arrays.fill(cbuf, BACKSPACE);
+ out.write(cbuf);
+
+ return;
+ } else if (buf.cursor == 0) {
+ return;
+ } else if (mask != null) {
+ c = mask.charValue();
+ } else {
+ printCharacters(buf.buffer.substring(buf.cursor - where, buf.cursor).toCharArray());
+ return;
+ }
+
+ // null character mask: don't output anything
+ if (NULL_MASK.equals(mask)) {
+ return;
+ }
+
+ printCharacters(c, Math.abs(where));
+ }
+
+ /**
+ * Read a character from the console.
+ *
+ * @return the character, or -1 if an EOF is received.
+ */
+ public final int readVirtualKey() throws IOException {
+ int c = terminal.readVirtualKey(in);
+
+ if (debugger != null) {
+ // debug("keystroke: " + c + "");
+ }
+
+ // clear any echo characters
+ clearEcho(c);
+
+ return c;
+ }
+
+ public final int readCharacter(final char[] allowed) throws IOException {
+ // if we restrict to a limited set and the current character
+ // is not in the set, then try again.
+ char c;
+
+ Arrays.sort(allowed); // always need to sort before binarySearch
+
+ while (Arrays.binarySearch(allowed, c = (char) readVirtualKey()) < 0);
+
+ return c;
+ }
+
+ /**
+ * Issue num deletes.
+ *
+ * @return the number of characters backed up
+ */
+ private final int delete(final int num)
+ throws IOException {
+ /* Commented out beacuse of DWA-2949:
+ if (buf.cursor == 0)
+ return 0;*/
+
+ buf.buffer.delete(buf.cursor, buf.cursor + 1);
+ drawBuffer(1);
+
+ return 1;
+ }
+
+ public final boolean replace(int num, String replacement) {
+ buf.buffer.replace(buf.cursor - num, buf.cursor, replacement);
+ try {
+ moveCursor(-num);
+ drawBuffer(Math.max(0, num - replacement.length()));
+ moveCursor(replacement.length());
+ } catch (IOException e) {
+ e.printStackTrace();
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Issue a delete.
+ *
+ * @return true if successful
+ */
+ public final boolean delete()
+ throws IOException {
+ return delete(1) == 1;
+ }
+
+ public void setHistory(final History history) {
+ this.history = history;
+ }
+
+ public History getHistory() {
+ return this.history;
+ }
+
+ public void setCompletionHandler(final CompletionHandler completionHandler) {
+ this.completionHandler = completionHandler;
+ }
+
+ public CompletionHandler getCompletionHandler() {
+ return this.completionHandler;
+ }
+
+ /**
+ *
+ * myConsoleReader.setEchoCharacter(new Character('*'));
+ *
+ *
+ *
+ * null
+ *
+ *
+ * will restore normal character echoing. Setting the character to
+ *
+ *
+ * new Character(0)
+ *
+ *
+ * will cause nothing to be echoed.
+ *
+ * A pass-through application that sets the system input stream to a + * {@link ConsoleReader} and invokes the specified main method. + *
+ * @author Marc Prud'hommeaux + */ +public class ConsoleRunner { + private static ConsoleReader reader; + + public static ConsoleReader getReader() { return reader; } + + public static final String property = "jline.history"; + + public static void main(final String[] args) throws Exception { + String historyFileName = null; + + List argList = new ArrayList(Arrays.asList(args)); + + if (argList.size() == 0) { + usage(); + + return; + } + + historyFileName = System.getProperty(ConsoleRunner.property, null); + + // invoke the main() method + String mainClass = (String) argList.remove(0); + + // setup the inpout stream + reader = new ConsoleReader(); + + if (historyFileName != null) { + reader.setHistory(new History (new File + (System.getProperty("user.home"), + ".jline-" + mainClass + + "." + historyFileName + ".history"))); + } else { + reader.setHistory(new History(new File + (System.getProperty("user.home"), + ".jline-" + mainClass + ".history"))); + } + + String completors = System.getProperty + (ConsoleRunner.class.getName() + ".completors", ""); + List completorList = new ArrayList(); + + for (StringTokenizer tok = new StringTokenizer(completors, ","); + tok.hasMoreTokens();) { + completorList.add + ((Completor) Class.forName(tok.nextToken()).newInstance()); + } + + if (completorList.size() > 0) { + reader.addCompletor(new ArgumentCompletor(completorList)); + } + + ConsoleReaderInputStream.setIn(reader); + + try { + Class.forName(mainClass). + getMethod("main", new Class[] { String[].class }). + invoke(null, new Object[] { argList.toArray(new String[0]) }); + } finally { + // just in case this main method is called from another program + ConsoleReaderInputStream.restoreIn(); + } + } + + private static void usage() { + System.out.println("Usage: \n java " + "[-Djline.history='name'] " + + ConsoleRunner.class.getName() + + "+ * This completor tries to behave as similar as possible to + * bash's file name completion (using GNU readline) + * with the following exceptions: + * + *
TODO
+ *+ * A completor that contains multiple embedded completors. This differs + * from the {@link ArgumentCompletor}, in that the nested completors + * are dispatched individually, rather than delimited by arguments. + *
+ * + * @author Marc Prud'hommeaux + */ +public class MultiCompletor implements Completor { + Completor[] completors = new Completor[0]; + + /** + * Construct a MultiCompletor with no embedded completors. + */ + public MultiCompletor() { + this(new Completor[0]); + } + + /** + * Construct a MultiCompletor with the specified list of + * {@link Completor} instances. + */ + public MultiCompletor(final List completors) { + this((Completor[]) completors.toArray(new Completor[completors.size()])); + } + + /** + * Construct a MultiCompletor with the specified + * {@link Completor} instances. + */ + public MultiCompletor(final Completor[] completors) { + this.completors = completors; + } + + public int complete(final String buffer, final int pos, final List cand) { + int[] positions = new int[completors.length]; + List[] copies = new List[completors.length]; + + for (int i = 0; i < completors.length; i++) { + // clone and save the candidate list + copies[i] = new LinkedList(cand); + positions[i] = completors[i].complete(buffer, pos, copies[i]); + } + + int maxposition = -1; + + for (int i = 0; i < positions.length; i++) { + maxposition = Math.max(maxposition, positions[i]); + } + + // now we have the max cursor value: build up all the + // candidate lists that have the same cursor value + for (int i = 0; i < copies.length; i++) { + if (positions[i] == maxposition) { + cand.addAll(copies[i]); + } + } + + return maxposition; + } + + public void setCompletors(final Completor[] completors) { + this.completors = completors; + } + + public Completor[] getCompletors() { + return this.completors; + } +} diff --git a/lib/src/src/main/java/jline/NullCompletor.java b/lib/src/src/main/java/jline/NullCompletor.java new file mode 100644 index 0000000..aa6cdf7 --- /dev/null +++ b/lib/src/src/main/java/jline/NullCompletor.java @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved. + * + * This software is distributable under the BSD license. See the terms of the + * BSD license in the documentation provided with this software. + */ +package jline; + +import java.util.*; + +/** + *+ * A completor that does nothing. Useful as the last item in an + * {@link ArgumentCompletor}. + *
+ * + * @author Marc Prud'hommeaux + */ +public class NullCompletor implements Completor { + /** + * Returns -1 always, indicating that the the buffer is never + * handled. + */ + public int complete(final String buffer, int cursor, List candidates) { + return -1; + } +} diff --git a/lib/src/src/main/java/jline/SimpleCompletor.java b/lib/src/src/main/java/jline/SimpleCompletor.java new file mode 100644 index 0000000..2d488b6 --- /dev/null +++ b/lib/src/src/main/java/jline/SimpleCompletor.java @@ -0,0 +1,194 @@ +/* + * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved. + * + * This software is distributable under the BSD license. See the terms of the + * BSD license in the documentation provided with this software. + */ +package jline; + +import java.io.*; +import java.util.*; + +/** + *+ * A simple {@link Completor} implementation that handles a pre-defined + * list of completion words. + *
+ * + *+ * Example usage: + *
+ *
+ * myConsoleReader.addCompletor (new SimpleCompletor (new String [] { "now", "yesterday", "tomorrow" }));
+ *
+ *
+ * @author Marc Prud'hommeaux
+ */
+public class SimpleCompletor implements Completor, Cloneable {
+ /**
+ * The list of candidates that will be completed.
+ */
+ SortedSet candidates;
+
+ /**
+ * A delimiter to use to qualify completions.
+ */
+ String delimiter;
+ final SimpleCompletorFilter filter;
+
+ /**
+ * Create a new SimpleCompletor with a single possible completion
+ * values.
+ */
+ public SimpleCompletor(final String candidateString) {
+ this(new String[] {
+ candidateString
+ });
+ }
+
+ /**
+ * Create a new SimpleCompletor with a list of possible completion
+ * values.
+ */
+ public SimpleCompletor(final String[] candidateStrings) {
+ this(candidateStrings, null);
+ }
+
+ public SimpleCompletor(final String[] strings,
+ final SimpleCompletorFilter filter) {
+ this.filter = filter;
+ setCandidateStrings(strings);
+ }
+
+ /**
+ * Complete candidates using the contents of the specified Reader.
+ */
+ public SimpleCompletor(final Reader reader) throws IOException {
+ this(getStrings(reader));
+ }
+
+ /**
+ * Complete candidates using the whitespearated values in
+ * read from the specified Reader.
+ */
+ public SimpleCompletor(final InputStream in) throws IOException {
+ this(getStrings(new InputStreamReader(in)));
+ }
+
+ private static String[] getStrings(final Reader in)
+ throws IOException {
+ final Reader reader =
+ (in instanceof BufferedReader) ? in : new BufferedReader(in);
+
+ List words = new LinkedList();
+ String line;
+
+ while ((line = ((BufferedReader) reader).readLine()) != null) {
+ for (StringTokenizer tok = new StringTokenizer(line);
+ tok.hasMoreTokens(); words.add(tok.nextToken())) {
+ ;
+ }
+ }
+
+ return (String[]) words.toArray(new String[words.size()]);
+ }
+
+ public int complete(final String buffer, final int cursor, final List clist) {
+ String start = (buffer == null) ? "" : buffer;
+
+ SortedSet matches = candidates.tailSet(start);
+
+ for (Iterator i = matches.iterator(); i.hasNext();) {
+ String can = (String) i.next();
+
+ if (!(can.startsWith(start))) {
+ break;
+ }
+
+ if (delimiter != null) {
+ int index = can.indexOf(delimiter, cursor);
+
+ if (index != -1) {
+ can = can.substring(0, index + 1);
+ }
+ }
+
+ clist.add(can);
+ }
+
+ if (clist.size() == 1) {
+ clist.set(0, ((String) clist.get(0)) + " ");
+ }
+
+ // the index of the completion is always from the beginning of
+ // the buffer.
+ return (clist.size() == 0) ? (-1) : 0;
+ }
+
+ public void setDelimiter(final String delimiter) {
+ this.delimiter = delimiter;
+ }
+
+ public String getDelimiter() {
+ return this.delimiter;
+ }
+
+ public void setCandidates(final SortedSet candidates) {
+ if (filter != null) {
+ TreeSet filtered = new TreeSet();
+
+ for (Iterator i = candidates.iterator(); i.hasNext();) {
+ String element = (String) i.next();
+ element = filter.filter(element);
+
+ if (element != null) {
+ filtered.add(element);
+ }
+ }
+
+ this.candidates = filtered;
+ } else {
+ this.candidates = candidates;
+ }
+ }
+
+ public SortedSet getCandidates() {
+ return Collections.unmodifiableSortedSet(this.candidates);
+ }
+
+ public void setCandidateStrings(final String[] strings) {
+ setCandidates(new TreeSet(Arrays.asList(strings)));
+ }
+
+ public void addCandidateString(final String candidateString) {
+ final String string =
+ (filter == null) ? candidateString : filter.filter(candidateString);
+
+ if (string != null) {
+ candidates.add(string);
+ }
+ }
+
+ public Object clone() throws CloneNotSupportedException {
+ return super.clone();
+ }
+
+ /**
+ * Filter for elements in the completor.
+ *
+ * @author Marc Prud'hommeaux
+ */
+ public static interface SimpleCompletorFilter {
+ /**
+ * Filter the specified String. To not filter it, return the
+ * same String as the parameter. To exclude it, return null.
+ */
+ public String filter(String element);
+ }
+
+ public static class NoOpFilter implements SimpleCompletorFilter {
+ public String filter(final String element) {
+ return element;
+ }
+ }
+}
diff --git a/lib/src/src/main/java/jline/Terminal.java b/lib/src/src/main/java/jline/Terminal.java
new file mode 100644
index 0000000..6eecbe0
--- /dev/null
+++ b/lib/src/src/main/java/jline/Terminal.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved.
+ *
+ * This software is distributable under the BSD license. See the terms of the
+ * BSD license in the documentation provided with this software.
+ */
+package jline;
+
+import java.io.*;
+
+/**
+ * Representation of the input terminal for a platform. Handles
+ * any initialization that the platform may need to perform
+ * in order to allow the {@link ConsoleReader} to correctly handle
+ * input.
+ *
+ * @author Marc Prud'hommeaux
+ */
+public abstract class Terminal implements ConsoleOperations {
+ private static Terminal term;
+
+ /**
+ * @see #setupTerminal
+ */
+ public static Terminal getTerminal() {
+ return setupTerminal();
+ }
+
+ /**
+ * Reset the current terminal to null.
+ */
+ public static void resetTerminal() {
+ term = null;
+ }
+
+ /**
+ * Configure and return the {@link Terminal} instance for the + * current platform. This will initialize any system settings + * that are required for the console to be able to handle + * input correctly, such as setting tabtop, buffered input, and + * character echo.
+ * + *This class will use the Terminal implementation specified in the + * jline.terminal system property, or, if it is unset, by + * detecting the operating system from the os.name + * system property and instantiating either the + * {@link WindowsTerminalTest} or {@link UnixTerminal}. + * + * @see #initializeTerminal + */ + public static synchronized Terminal setupTerminal() { + if (term != null) { + return term; + } + + final Terminal t; + + String os = System.getProperty("os.name").toLowerCase(); + String termProp = System.getProperty("jline.terminal"); + + if ((termProp != null) && (termProp.length() > 0)) { + try { + t = (Terminal) Class.forName(termProp).newInstance(); + } catch (Exception e) { + throw (IllegalArgumentException) new IllegalArgumentException(e + .toString()).fillInStackTrace(); + } + } else if (os.indexOf("windows") != -1) { + t = new WindowsTerminal(); + } else { + t = new UnixTerminal(); + } + + try { + t.initializeTerminal(); + } catch (Exception e) { + e.printStackTrace(); + + return term = new UnsupportedTerminal(); + } + + return term = t; + } + + /** + * Returns true if the current console supports ANSI + * codes. + */ + public boolean isANSISupported() { + return true; + } + + /** + * Read a single character from the input stream. This might + * enable a terminal implementation to better handle nuances of + * the console. + */ + public int readCharacter(final InputStream in) throws IOException { + return in.read(); + } + + /** + * Reads a virtual key from the console. Typically, this will + * just be the raw character that was entered, but in some cases, + * multiple input keys will need to be translated into a single + * virtual key. + * + * @param in the InputStream to read from + * @return the virtual key (e.g., {@link ConsoleOperations#VK_UP}) + */ + public int readVirtualKey(InputStream in) throws IOException { + return readCharacter(in); + } + + /** + * Initialize any system settings + * that are required for the console to be able to handle + * input correctly, such as setting tabtop, buffered input, and + * character echo. + */ + public abstract void initializeTerminal() throws Exception; + + /** + * Returns the current width of the terminal (in characters) + */ + public abstract int getTerminalWidth(); + + /** + * Returns the current height of the terminal (in lines) + */ + public abstract int getTerminalHeight(); + + /** + * Returns true if this terminal is capable of initializing the + * terminal to use jline. + */ + public abstract boolean isSupported(); + + /** + * Returns true if the terminal will echo all characters type. + */ + public abstract boolean getEcho(); + + /** + * Invokes before the console reads a line with the prompt and mask. + */ + public void beforeReadLine(ConsoleReader reader, String prompt, + Character mask) { + } + + /** + * Invokes after the console reads a line with the prompt and mask. + */ + public void afterReadLine(ConsoleReader reader, String prompt, + Character mask) { + } + + /** + * Returns false if character echoing is disabled. + */ + public abstract boolean isEchoEnabled(); + + + /** + * Enable character echoing. This can be used to re-enable character + * if the ConsoleReader is no longer being used. + */ + public abstract void enableEcho(); + + + /** + * Disable character echoing. This can be used to manually re-enable + * character if the ConsoleReader has been disabled. + */ + public abstract void disableEcho(); + + public InputStream getDefaultBindings() { + // Mac bindings are slightly different from Unix/Linux. + // For instance, the Delete key behavior is different between them. + return Terminal.class.getResourceAsStream( + System.getProperty("os.name").toLowerCase().startsWith("mac") ? + "keybindings-mac.properties" : "keybindings.properties"); + } +} diff --git a/lib/src/src/main/java/jline/UnixTerminal.java b/lib/src/src/main/java/jline/UnixTerminal.java new file mode 100644 index 0000000..83f5194 --- /dev/null +++ b/lib/src/src/main/java/jline/UnixTerminal.java @@ -0,0 +1,433 @@ +/* + * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved. + * + * This software is distributable under the BSD license. See the terms of the + * BSD license in the documentation provided with this software. + */ +package jline; + +import java.io.*; +import java.util.*; + +/** + *
+ * Terminal that is used for unix platforms. Terminal initialization + * is handled by issuing the stty command against the + * /dev/tty file to disable character echoing and enable + * character input. All known unix systems (including + * Linux and Macintosh OS X) support the stty), so this + * implementation should work for an reasonable POSIX system. + *
+ * + * @author Marc Prud'hommeaux + * @author Updates Dale Kemp 2005-12-03 + */ +public class UnixTerminal extends Terminal { + public static final short ARROW_START = 27; + public static final short ARROW_PREFIX = 91; + public static final short ARROW_LEFT = 68; + public static final short ARROW_RIGHT = 67; + public static final short ARROW_UP = 65; + public static final short ARROW_DOWN = 66; + public static final short O_PREFIX = 79; + public static final short HOME_CODE = 72; + public static final short END_CODE = 70; + + public static final short DEL_THIRD = 51; + public static final short DEL_SECOND = 126; + + private boolean echoEnabled; + private String ttyConfig; + private String ttyProps; + private long ttyPropsLastFetched; + private boolean backspaceDeleteSwitched = false; + private static String sttyCommand = + System.getProperty("jline.sttyCommand", "stty"); + + + String encoding = System.getProperty("input.encoding", "UTF-8"); + ReplayPrefixOneCharInputStream replayStream = new ReplayPrefixOneCharInputStream(encoding); + InputStreamReader replayReader; + + public UnixTerminal() { + try { + replayReader = new InputStreamReader(replayStream, encoding); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + protected void checkBackspace(){ + String[] ttyConfigSplit = ttyConfig.split(":|="); + backspaceDeleteSwitched = ttyConfigSplit.length >= 7 && "7f".equals(ttyConfigSplit[6]); + } + + /** + * Remove line-buffered input by invoking "stty -icanon min 1" + * against the current terminal. + */ + public void initializeTerminal() throws IOException, InterruptedException { + // save the initial tty configuration + ttyConfig = stty("-g"); + + // sanity check + if ((ttyConfig.length() == 0) + || ((ttyConfig.indexOf("=") == -1) + && (ttyConfig.indexOf(":") == -1))) { + throw new IOException("Unrecognized stty code: " + ttyConfig); + } + + checkBackspace(); + + // set the console to be character-buffered instead of line-buffered + stty("-icanon min 1"); + + // disable character echoing + stty("-echo"); + echoEnabled = false; + + // at exit, restore the original tty configuration (for JDK 1.3+) + try { + Runtime.getRuntime().addShutdownHook(new Thread() { + public void start() { + try { + restoreTerminal(); + } catch (Exception e) { + consumeException(e); + } + } + }); + } catch (AbstractMethodError ame) { + // JDK 1.3+ only method. Bummer. + consumeException(ame); + } + } + + /** + * Restore the original terminal configuration, which can be used when + * shutting down the console reader. The ConsoleReader cannot be + * used after calling this method. + */ + public void restoreTerminal() throws Exception { + if (ttyConfig != null) { + stty(ttyConfig); + ttyConfig = null; + } + resetTerminal(); + } + + + + public int readVirtualKey(InputStream in) throws IOException { + int c = readCharacter(in); + + if (backspaceDeleteSwitched) + if (c == DELETE) + c = BACKSPACE; + else if (c == BACKSPACE) + c = DELETE; + + // in Unix terminals, arrow keys are represented by + // a sequence of 3 characters. E.g., the up arrow + // key yields 27, 91, 68 + if (c == ARROW_START && in.available() > 0) { + // Escape key is also 27, so we use InputStream.available() + // to distinguish those. If 27 represents an arrow, there + // should be two more chars immediately available. + while (c == ARROW_START) { + c = readCharacter(in); + } + if (c == ARROW_PREFIX || c == O_PREFIX) { + c = readCharacter(in); + if (c == ARROW_UP) { + return CTRL_P; + } else if (c == ARROW_DOWN) { + return CTRL_N; + } else if (c == ARROW_LEFT) { + return CTRL_B; + } else if (c == ARROW_RIGHT) { + return CTRL_F; + } else if (c == HOME_CODE) { + return CTRL_A; + } else if (c == END_CODE) { + return CTRL_E; + } else if (c == DEL_THIRD) { + c = readCharacter(in); // read 4th + return DELETE; + } + } + } + // handle unicode characters, thanks for a patch from amyi@inf.ed.ac.uk + if (c > 128) { + // handle unicode characters longer than 2 bytes, + // thanks to Marc.Herbert@continuent.com + replayStream.setInput(c, in); +// replayReader = new InputStreamReader(replayStream, encoding); + c = replayReader.read(); + + } + + return c; + } + + /** + * No-op for exceptions we want to silently consume. + */ + private void consumeException(Throwable e) { + } + + public boolean isSupported() { + return true; + } + + public boolean getEcho() { + return false; + } + + /** + * Returns the value of "stty size" width param. + * + * Note: this method caches the value from the + * first time it is called in order to increase speed, which means + * that changing to size of the terminal will not be reflected + * in the console. + */ + public int getTerminalWidth() { + int val = -1; + + try { + val = getTerminalProperty("columns"); + } catch (Exception e) { + } + + if (val == -1) { + val = 80; + } + + return val; + } + + /** + * Returns the value of "stty size" height param. + * + * Note: this method caches the value from the + * first time it is called in order to increase speed, which means + * that changing to size of the terminal will not be reflected + * in the console. + */ + public int getTerminalHeight() { + int val = -1; + + try { + val = getTerminalProperty("rows"); + } catch (Exception e) { + } + + if (val == -1) { + val = 24; + } + + return val; + } + + private int getTerminalProperty(String prop) + throws IOException, InterruptedException { + // tty properties are cached so we don't have to worry too much about getting term widht/height + if (ttyProps == null || System.currentTimeMillis() - ttyPropsLastFetched > 1000) { + ttyProps = stty("-a"); + ttyPropsLastFetched = System.currentTimeMillis(); + } + // need to be able handle both output formats: + // speed 9600 baud; 24 rows; 140 columns; + // and: + // speed 38400 baud; rows = 49; columns = 111; ypixels = 0; xpixels = 0; + for (StringTokenizer tok = new StringTokenizer(ttyProps, ";\n"); + tok.hasMoreTokens();) { + String str = tok.nextToken().trim(); + + if (str.startsWith(prop)) { + int index = str.lastIndexOf(" "); + + return Integer.parseInt(str.substring(index).trim()); + } else if (str.endsWith(prop)) { + int index = str.indexOf(" "); + + return Integer.parseInt(str.substring(0, index).trim()); + } + } + + return -1; + } + + /** + * Execute the stty command with the specified arguments + * against the current active terminal. + */ + protected static String stty(final String args) + throws IOException, InterruptedException { + return exec("stty " + args + " < /dev/tty").trim(); + } + + /** + * Execute the specified command and return the output + * (both stdout and stderr). + */ + private static String exec(final String cmd) + throws IOException, InterruptedException { + return exec(new String[] { + "sh", + "-c", + cmd + }); + } + + /** + * Execute the specified command and return the output + * (both stdout and stderr). + */ + private static String exec(final String[] cmd) + throws IOException, InterruptedException { + ByteArrayOutputStream bout = new ByteArrayOutputStream(); + + Process p = Runtime.getRuntime().exec(cmd); + int c; + InputStream in = null; + InputStream err = null; + OutputStream out = null; + + try { + in = p.getInputStream(); + + while ((c = in.read()) != -1) { + bout.write(c); + } + + err = p.getErrorStream(); + + while ((c = err.read()) != -1) { + bout.write(c); + } + + out = p.getOutputStream(); + + p.waitFor(); + } finally { + try {in.close();} catch (Exception e) {} + try {err.close();} catch (Exception e) {} + try {out.close();} catch (Exception e) {} + } + + String result = new String(bout.toByteArray()); + + return result; + } + + /** + * The command to use to set the terminal options. Defaults + * to "stty", or the value of the system property "jline.sttyCommand". + */ + public static void setSttyCommand(String cmd) { + sttyCommand = cmd; + } + + /** + * The command to use to set the terminal options. Defaults + * to "stty", or the value of the system property "jline.sttyCommand". + */ + public static String getSttyCommand() { + return sttyCommand; + } + + public synchronized boolean isEchoEnabled() { + return echoEnabled; + } + + + public synchronized void enableEcho() { + try { + stty("echo"); + echoEnabled = true; + } catch (Exception e) { + consumeException(e); + } + } + + public synchronized void disableEcho() { + try { + stty("-echo"); + echoEnabled = false; + } catch (Exception e) { + consumeException(e); + } + } + + /** + * This is awkward and inefficient, but probably the minimal way to add + * UTF-8 support to JLine + * + * @author Marc Herbert + */ + static class ReplayPrefixOneCharInputStream extends InputStream { + byte firstByte; + int byteLength; + InputStream wrappedStream; + int byteRead; + + final String encoding; + + public ReplayPrefixOneCharInputStream(String encoding) { + this.encoding = encoding; + } + + public void setInput(int recorded, InputStream wrapped) throws IOException { + this.byteRead = 0; + this.firstByte = (byte) recorded; + this.wrappedStream = wrapped; + + byteLength = 1; + if (encoding.equalsIgnoreCase("UTF-8")) + setInputUTF8(recorded, wrapped); + else if (encoding.equalsIgnoreCase("UTF-16")) + byteLength = 2; + else if (encoding.equalsIgnoreCase("UTF-32")) + byteLength = 4; + } + + + public void setInputUTF8(int recorded, InputStream wrapped) throws IOException { + // 110yyyyy 10zzzzzz + if ((firstByte & (byte) 0xE0) == (byte) 0xC0) + this.byteLength = 2; + // 1110xxxx 10yyyyyy 10zzzzzz + else if ((firstByte & (byte) 0xF0) == (byte) 0xE0) + this.byteLength = 3; + // 11110www 10xxxxxx 10yyyyyy 10zzzzzz + else if ((firstByte & (byte) 0xF8) == (byte) 0xF0) + this.byteLength = 4; + else + throw new IOException("invalid UTF-8 first byte: " + firstByte); + } + + public int read() throws IOException { + if (available() == 0) + return -1; + + byteRead++; + + if (byteRead == 1) + return firstByte; + + return wrappedStream.read(); + } + + /** + * InputStreamReader is greedy and will try to read bytes in advance. We + * do NOT want this to happen since we use a temporary/"losing bytes" + * InputStreamReader above, that's why we hide the real + * wrappedStream.available() here. + */ + public int available() { + return byteLength - byteRead; + } + } +} diff --git a/lib/src/src/main/java/jline/UnsupportedTerminal.java b/lib/src/src/main/java/jline/UnsupportedTerminal.java new file mode 100644 index 0000000..2d87a18 --- /dev/null +++ b/lib/src/src/main/java/jline/UnsupportedTerminal.java @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved. + * + * This software is distributable under the BSD license. See the terms of the + * BSD license in the documentation provided with this software. + */ +package jline; + +import java.io.IOException; + +/** + * A no-op unsupported terminal. + * + * @author Marc Prud'hommeaux + */ +public class UnsupportedTerminal extends Terminal { + private Thread maskThread = null; + + public void initializeTerminal() { + // nothing we need to do (or can do) for windows. + } + + public boolean getEcho() { + return true; + } + + + public boolean isEchoEnabled() { + return true; + } + + + public void enableEcho() { + } + + + public void disableEcho() { + } + + + /** + * Always returng 80, since we can't access this info on Windows. + */ + public int getTerminalWidth() { + return 80; + } + + /** + * Always returng 24, since we can't access this info on Windows. + */ + public int getTerminalHeight() { + return 80; + } + + public boolean isSupported() { + return false; + } + + public void beforeReadLine(final ConsoleReader reader, final String prompt, + final Character mask) { + if ((mask != null) && (maskThread == null)) { + final String fullPrompt = "\r" + prompt + + " " + + " " + + " " + + "\r" + prompt; + + maskThread = new Thread("JLine Mask Thread") { + public void run() { + while (!interrupted()) { + try { + reader.out.write(fullPrompt); + reader.out.flush(); + sleep(3); + } catch (IOException ioe) { + return; + } catch (InterruptedException ie) { + return; + } + } + } + }; + + maskThread.setPriority(Thread.MAX_PRIORITY); + maskThread.setDaemon(true); + maskThread.start(); + } + } + + public void afterReadLine(final ConsoleReader reader, final String prompt, + final Character mask) { + if ((maskThread != null) && maskThread.isAlive()) { + maskThread.interrupt(); + } + + maskThread = null; + } +} diff --git a/lib/src/src/main/java/jline/WindowsTerminal.java b/lib/src/src/main/java/jline/WindowsTerminal.java new file mode 100644 index 0000000..d036088 --- /dev/null +++ b/lib/src/src/main/java/jline/WindowsTerminal.java @@ -0,0 +1,520 @@ +/* + * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved. + * + * This software is distributable under the BSD license. See the terms of the + * BSD license in the documentation provided with this software. + */ +package jline; + +import java.io.*; + +import jline.UnixTerminal.ReplayPrefixOneCharInputStream; + +/** + *+ * Terminal implementation for Microsoft Windows. Terminal initialization in + * {@link #initializeTerminal} is accomplished by extracting the + * jline_version.dll, saving it to the system temporary + * directoy (determined by the setting of the java.io.tmpdir System + * property), loading the library, and then calling the Win32 APIs SetConsoleMode and + * GetConsoleMode to + * disable character echoing. + *
+ * + *
+ * By default, the {@link #readCharacter} method will attempt to test to see if
+ * the specified {@link InputStream} is {@link System#in} or a wrapper around
+ * {@link FileDescriptor#in}, and if so, will bypass the character reading to
+ * directly invoke the readc() method in the JNI library. This is so the class
+ * can read special keys (like arrow keys) which are otherwise inaccessible via
+ * the {@link System#in} stream. Using JNI reading can be bypassed by setting
+ * the jline.WindowsTerminal.directConsole system property
+ * to false.
+ *
+The core JLine API. The central class is +jline.ConsoleReader}, which +is a reader for obtaining input from an arbitrary +InputStream (usually System.in). +
+ + diff --git a/lib/src/src/main/native/Makefile b/lib/src/src/main/native/Makefile new file mode 100644 index 0000000..c620814 --- /dev/null +++ b/lib/src/src/main/native/Makefile @@ -0,0 +1,8 @@ + + +#export PATH=${PATH}:/usr/lib/gcc-lib/i686-pc-cygwin/3.3.3 +JDK='/C/Program Files/Java/jdk1.5.0/' + +native: + #gcc -I'C:/Program Files/Java/'*'/include/' -I'C:/Program Files/Java/'*'/include//win32/' -mno-cygwin -Wl,--add-stdcall-alias -shared -o jline.dll jline_WindowsTerminal.c + gcc -L /usr/lib/mingw/ -I${JDK}/include -I${JDK}/include/win32 -mwindows -mno-cygwin -Wl,--add-stdcall-alias -shared -o jline.dll jline_WindowsTerminal.c diff --git a/lib/src/src/main/native/jline_WindowsTerminal.c b/lib/src/src/main/native/jline_WindowsTerminal.c new file mode 100644 index 0000000..4e78a66 --- /dev/null +++ b/lib/src/src/main/native/jline_WindowsTerminal.c @@ -0,0 +1,57 @@ +#include "jline_WindowsTerminal.h" +#includem%s`$-2;R@rje1xD6M{M>=;gl4{8E=Y6o1cw^RHzX13K^Oj-O zM0~hd+xMF1J%IeM{mgt4k1>Z(S_lW#~ aB@gxa_A>ZI{b}0IM=|BCy>tM`Xy}t zb{=6Xm1=SYX{RgUVGs&XzAjdmDxxu}i@r1Y$b{{?cvU3@XOr4Cc&0t-*VsFS?Y{wB zQumUcD58V-rNq->;^`VFAt&(^rEW(lEL+F0D9R@AYfji+LKTT$N9CDyIZPfV^l6I$ z3FRc*l|1tKq&CE)(r)VaUi2$itc-%pz;5Tw_U{qLwi3UdqJ2hEN(DbHq(~Go+?wFH z#qH1G(q2)9LczSH6yWF$2sNMW^bO5KxA|Ou>Z|6MOGN$5|JC|5#A1?3fyEhV{7{aY zto~1(h3d@~k9uLDT9M }gAL<${*YGob=P>mWH* z9`$C2XHpN5xPBSPOXK=lo%^+v+vU4b&{=!Al>4R63YD?J;_7i{m>kNbY6=u`65#Ur zAsjU>&e#Yl|I6SBB)P$m!i2r9a7SF1zNa|y-XNA!7*@F3uohvh6g8^i4hx|ZVx9q^ zAV?nKwt^TY^^~^yGhJ-q3O %Bt727LIHy^gj8C; z43fMoN&VyFKt~Gvg#vYs$$!dXYfPeEO6@Of0?4`pzpjK=3UNl9dwA2O9$3)mj?WK2 zk>cV`=(e=NfIFdAwQiwG<~sZ)>_dgykEurWg{M(2?_S&)R8rghly>Ij)Lk5 zv^)=T83i>EXigq1mnf)_Kv(1i<3O7Ul#%zNIM7`L8kC1EQj3x-dfWn#`V;D#d(UwL zMG6Sop}n#K9L2M638WHN`xQS+!5DkV{3J(v{V6_s%v~R18G Gi<-X11M>vuw6pbtseDc9}n9v zmSXWtDuU^q6Unpz;3Y}$W*?`z(bX2<^Nvc}vvn-IxhK3f_fc { zg5EH90_%ECzhofZR~dBGj=#B9oYRcpm8_69D1MU_Uo znK!xq(eHGma+(sc&QW1tPH2C~$0csR)5mk7>x4-CA{-VcpvJ#J>8RgG3D_r2G@}ELf94q` zo7BCbTXtlkmgXe2B)lFrt?k2bSX?m#1DEc=ATSNHi8b?T$`n3USW2~bEZpFMDm|e6 z=~Flw1F@PtA)6;K`b-F3)dSj(kqLfUFGSyA`wLfN>S1-^J3xL#?{>%?jbY^K6el_A zKSjmvTd3k@RHSlo_ou|1DLsa@`eKxk5{^s$nJ1VsNIL{8mz+33X^?jK9U?FFgy@qd z4tP5gtwnfCA
{fMD(?m*R26s5M8}z7J6#n+o?Y5H@NI&B7lC z=uX}u-ORe5ir4QZjQEzlpFERS>uKb{?+1q2K#5B|hCLeA9qrPO_^Km2yz2cKoham1 z55TMw?m#aL4M(`6i>&n>WRPl`ClF%yWOO1Cw!5icpy`Yf&GSL@cC-Ydoe`pO`GY72 z`B_9XRi{J_?u)u3ME?UZ&cd+$bwuNdPC>Ug{{%Ef^dm@-bAu?u8k~kTI2~)S18Z=9 zdt97X^BsL~PP+E)Cp6~po*vbF?-TjQp*S;PWFr`XKp7CsPbX=*p$(oEpi0|kp45fn zd-E-Fo`=1z_Qg>l(Qnh1`Uqy6SJlAw7D~1u2}gn_caLrJgTw>1*E8u`Z*YUf ?f*_{uEWO`^akDqze$uFU`ux)0S! zRysTf;u2%rt}ix2qVrKRQoZOrjFceflGct{C!IVxs^#QSTysUCn2VhAGh!dVyn(To zNwYv}YDb`{peeC^{lXvx@_Q9rnT#{6^$uWU)K4$G9w$Jh3ue~OOs3~Ol;l pGhb=jVG4lS@fzcjI+@%Wsem!-?&)CH;D+#|kZ)n!T8 z e(z+?rXCd%*WTmS{ 6;}=bhB*Q9HDIk;FjB zHr1KYs*O9@;qNr?8=_GbMEQsNpxOKDb0E i6h0hc z+Cm3X(RSnc59Ia$5v}e$R125~HN_Q#ej7mPfHheJDI!QkKvY6}Pu-2}s0mU+ZPIwf zE#;A2h (jy z_yepx0kn^9HY0c$HYJ0nt|sI`#zv8GbDs>l#I{;w6!*y(j*M$W#{BpU&2xfJ#L#jr zOj91tJX!%@opn{CJ4tQcN_ ||H4FhTqDZSW0l8w;*Y7sJWc?El6h5A%PMA`kL^W0F(7 zm|U>Cz_k!AKfm-iQ1OBR*racxI7%x+-0C(mN!#zDU#M^vGV>agiNmw$Kw3Ra2cob; zRr3@i6ZXLhzep;5g&PfbZABgI|Mpp6g+s4rsa7)zg 8xlfGLAqQ9$rVbm;_* zTGa=sEA@aY{AS3^1428Q&^7~YWr#PxQKiC3?f^#*X2AsQm&eIUz(Fng!?m;6#XhYR z#OoIEMoE1(qYh;tu1?{JCNr(M&dJ!P_&$U00epXk?>q%%G7-1^`g EPY!?DhyRX^(Jf<$u93^%--OGr6muSgfx=^7uk;I!S}&OIbK zE20-G^zq>OVF@mrifb-KF76NZs$XhHfEBhA)5pB;C-k+zFEYqjf zd)VGBh{jB{HE;zIw7-cAPWz*|h`{~OaD>m@ecB(7fmqxF<^|v@FmFclu(PQgmcM&M z_PS{HTAsaJ&tAu~@8OuB?ls8N0*A#w#m0z;a%ct!mj6Xw)TrQci@i6W`oIprrnfeZ zT7$ <{pWSmmjUyPIq%@7EEL(62q{Pku^T0cYJ4Lq7rNA%|&vNga4Vbb);?*eXX zfEr`k7H+0m5AX`z+HI64dhUYe_Ozh2Ns!A`-G`E`H5{t|sP?)5qZoF8CL?;0nKi zB1v#~IPq)*H?*{`I?hhzC~>JM%j8SMqc--C8^41?R|3~d34`22@>K1lpi|Do#WCTa z8;krAdj4STZ9N};2HXiGQB|#>deHI`GQ6rzE>Oae;A@5I9uJ*BqojCv;ujfMdq_7F z(HYf>8~cbD7sf`c-}_}NP9J1AIa-t~cauSz08JY=WH3P;XeRP}z(?>5#_sdiO~>7B z=y L=rE3VwD3^5A!Gp7o z?+hB>S$$gUPq(QL-Q?Mfr}u}(^OHAz2-7G38IS4N$Fp(`FEBjCZ8@PbDa$Rr8ZD zV>u^O^RE#NSRal6v`u|F>rcd0=#17#;$oaW?SFqnQtLCh_B%y0{}$-YYZRKAt@1ia zy*rJv059>wO} t(R()VUU#VtUR%p3~-#{GS^X_?lnS{SSRkpu02}H4z!bY{HE*9E{ZyONh^=Za+>g z)q26^SXwYOmX5GWln=U=y0#Pg-k>!T=zFJ2-7+o;bL>zAUHCTwW2>Y}@vs($2U{ow zN_8`_qT63b3^GK-Mcmh;{UzOt>xU(! #}Yh_!*cUYU~+Q=&ot6ez|#oM z>Ub6b&nPU0XMG%==pk >hEzBmjb!b0># zp7#@VCR3gGO>)eh)HlJ`;Q_-hU#8=8EQ$P-P0JiaZphWq!F%Z%=gvBi uFefoC%wq9VwwQQKZ-fHoQF01sP}+;-nr=sx3`z3z+}hZK}E zuEA;e6x+6I3~t<)ap&&ZFw8Zn0~;~y)lD_@Sk*HQEjf*`Ems5oy+&N%IHDG$0>vD% zfojO;RSQfoG7!ro?^8_zSb#p^Y~yY??Mc|PD55T*T2Asy@nR?fYo5F6GU*t)#*OpN z{Qx<%o#W10!`Q!uYdam}>XjN%iFQ3iJ9(}M4=GqU2J7+j3ApPt1bgGrHR!c&Kw48h zaV0eNaib1KGj#(TIyh-B&*nx7jWe!~f%Y#9%5$Lw;4j+bOpT0haM2^dSq^{ihz$eL zbTB{C%t-%f4srzZJNvcFg{Rekr!sd*>OEpavkz($AccfFjk&l{v=--d{T*)H{^z2s zjmL%uUarWT8w??#jkP}K-~xVVI|d?DB`)da!+8a7@FN7HMzW;bF|R=};_4JVb=&Pv z2rI)d>DAT5No;m$eK2cYK5@;zLrE!cH?ORkf67xBe4(1rnCRorTHK61vRlkpY<%Jb zJT_n9zr^!{RO2l3HFBO2cV3{La!&2hE 8_M;Ezf}geo1cpo`8|q+j{C!BltFkdD8tll z{+XT^+Cx>7^r~W8h~0H`J^bDY1@4bsL&FU(q1i!fCpAE`NPS1^0RD|FYOk%4HZoH1 zw$U$ CqwBOZQF1_+PWd#HEEwG=uRI4yy`5t&qNv%2KiUen?`5OMalu(HX66%PB4Qk z-`KZK3R~)Muh8EDErAMO;P}11>rtp5d85C}Bro-MS>+rY|H|ojb^(Ymbx$Cl4+2J@ z>q$U#_;!;CqZG^faa`8mzFF(zvP_mfwQh*Z8q~Lz6Il=e8h`5gQY5EOHp##gM$iH6 zNN+gYl>s1m?#3d-?ZOySP05VS52ltuL+pTt@U*^f@&?B|h^T8Px?$dgePVx;gR2s) zvJ~MF);lLL`wB$Tu*l?gY&Z`a uCa(CfX9>e3zHb?O5N1?V-_?lc}_L{H<`{ zlN!b*@i!RJ`Y(vNI?v<`{}(1X-T#GEPNnw}jQlGxF*M|_#IR86Ux_&(^Ith5)~3!S z{1f(LNpK%BtqSC)@mL~agL%w?*iarzMr;_5r6QKjV*?QzNsPJ _MMN*j0-go-zA_R2kmapgV4|)Kf11t6^=~A<~lFTE=M#e? J8XPB3_%MoX4NWva>VrZ7?sIMD1?~jZcz|gfu3zE8tkwec z=T^H>ym1OIelHd04|ISKo`6sH=Oq}<+=DREis7;dB>{9dKvLi(Q98-ca~CN(qpfiW zTIn%Az6Pq$)|iNdQ2QF(<<07BQ^5Sn%|L&)C1BpeBdGy%3q@YXdgFk3^~K}Zf1NgO z8VS!S24Q~MDp#OEdK#?@$EhRqz9?INn&$GR5mauQd-~{EO$~ Dqiy;D8aSu(M z)m|0rA9>9Ctbg#2`t!cs2!<;|ci_%KSfk%|^Tz;zIB{Nfmh&BYT8AGzz9YM^0<^c_ zKmmtBUrM=cwOLv@4k*s*yulK|v G~mJ z?{Lc1F0yoH?Wao3Ei?-1cAAUi=*zXgvK)vGiC8+Lu~sW)O@U_v&scp|g1lj>pXO_$ znNgla+fSDaVJC9K=6jZo)3$97=2*^{R?Udhvv~7F-=IW|lh`gLPM~=-qV2JN#gogE z4%;@ fTt_qO;H} 5Vo=3uOL7B%Qq7bjx{Z*@okSEo0yK$BE=ayLPH(FuUv0QM?xptEsZH)C4Gx7RGy zpQZ=dea~%2gUpt0O17oDlbuq1YaP-+<5T!+sKjhumI<_+1n(rcMsR^xcppK#3EHdt!BekM51#s73T%Etvb!!dgLUnIjTl2W zLO;T0Pr| pI2jLnbkP8%V=U5WfWWw+q8pdR8yKX#$%m)5&+rhPG(O$o@CGqH7z-Mf z=rD=#X^k5nuihW=x0m<}hI^C2`o+mK2p)}Kodi2dunyqGM~H@R0S3}5FcSl00dUX{ zjn7VuPyg9;@L+Vrfc>XOv8oqlw-nNrx(w?m?X@*F2Ku0WH|6QRDQ-|T)3F3hyu}&m zZj?*&w6{9UKN%6Sh(1;|M+*y4h@_(^w3`>!fIs*|U0%qG!$)o9QnSdm?TBZGLH~Gc ze?APb(>GQ+dCdQQlAMSGdMS{GHw2Jm_&cw{JWf=mi$oF<3GyIn6!_A1f=}kJF_Gou zra@2-G<$pad=@?P`8pc&6&`~@QeXingb-kAAUuk1Cl@zN+`xive0!Cnq5E}yfd3K( z#zH@$3L-}-#VaAyDA7$ny-J6eHH#rdf+f1YT)9}A@X|S!&|zSf_7QxTQpC3`o^5;r zRs~jKIGb_r>Dlq(q6EL|)3xonX#4e_(k-SQ^FpX~173IoI~(sa+kpk={S#=CSi;}Y zr+t8Ak9iV`8RZqR+fk#?Fh6mMUKF|xA{DtECEPGhelH4VC|P#wglPZhATLY@x}pRQ zWcN5kD~_=J8AQc9LYlWnu#AcazaQaIJvoKyVXwMN#j_>*T>kfaA2-Q^aiasbKTIc& zdJ}fx=^pQSt)9}>d1@-W8MD&hW%VS?vDmh`5+1k7DcA}4JG-TaaO=jCeVE*k=K&dE za(wTOMIZUgwT@VZWo?ku+M2fNWNSFA7{6+hTKA`Q9fs-?{@G3gV~a3y4)G|pMY{_o z30%`@+9nZ6*JKfKXlq3zQ(Gw_S=x0xlA !DZX38AE)?s5r3NE zPl`C12ijH Y)+FLI+}iCTew5-hBHl^yG7;A(zEs4!Dee{VUWz+KoXk*d znuybmR?8G|a@cF5MLeD2X(H~RxJkq_Dc;M?A(dR=+NUB;n|Q5L#GMp>L&PPD?-TJN zioZy47$P_967z{X+OuL~JwVE}K5+w#AhUKKuZ81?T _Ya@=55XW9KDe8|flz5p$bt(ZF)dYekX*o3ITr_bkP9&fX&h3M{I* z0D7oU?RKlD=_N+*E4PZr1H8dPC=UAxxKUhm({U Sz7Ig9=9(hpyt}1Kd8!}Dbw9%~=F6I8??E`>7Azn-Ps;!^{JLgR92@Kn1tO6jT zv{QcUjz^{KSlH=vr#H8A!vk~SNKTVr*_jyIfMa-<4QF;6lB1<;5_FFChjj2}Qm2Fm z46tW9Zfm$64YVg5JV=fM?Vdk^>Eg^ixt+cHF3QqvVk$Qk<>m)X8RTC(pnU}|0AULp zIl}6zZhFp#?0G2BLtq=|w0qUUVmIzNsjs`pXZ0$;YH$5*-Mr!8+-{iepR~XW*&bS& z+a|*m&Kp$m4!|hQ+Kz!hf>7$UHT(&>onB+27NA*P)}_9!Mjqi $cj z=4{&7=~BB2)lRqiiLEh+F_Ho`Y1)G?k~lZDP+EO@I}fl|v5s9p1>E#_D6X>7V{DR~ z9&uQgKuDEjZRg)n7iBOPF1vCGHSjFIpn!t Gfc_K#lq$RO!-_lrIm4H4%tM0s%{f~DdB$Ps22axa9cBsD5HD@ zQJjkwZs7Fh!Yo$2h(gD?lhc$uM4`Rh$?3`z+ThTh=@8k;dMEP3k@}+|R@mprP73)c z8U3oMu%Y%zw*8n-qW1`4dKjaRkXEn*^s29TUSPsJtS4H>Z86N+GIRh4nXyZ`76zU? z4{pIZ(#h5&a8G%EPH^aeRrMc)ZL8`}_u4kVCNRuVQ9gH QhY^3%B}V0q$4gy48Tnx00b*E4+%{ 3X_bj_T>X(9 Ms*-)F_i zK{_kwk&VW%t>Hn`X;0EwaqLl@6?l7RWXZxKGVrqb;>pvOr`w4I*8nX@o>R~RoIra{ zV@rX05H2#Xi_%$2N|YlZYlK9duFHlJk2~w{)K{|kw30b?hQsxD=#Y5?83>3=-QD#f zn1okRc-0T-`BjM3#-Z-OrZn|Ak~f$Vi2$lKCJcgh1JnVG2e+#6+rIQB%-p?LWNi&( z|6zfw!Rq*G6f^B%l*j6bMN#gcQ3uIhwWaTHt^l@Pb<;)?(S4W>7Tdiaqhs8y>h!4l zZFj$e1ZGgwcEB0+EwCLUPePkp9LN*l+R_F-V{s9*=e!2lXus5#3^TXunJ( z>BPklFrd@sKA;}!J0wAD;h-$EMudfUP3yEmxUw(7omH(CkkCZYwvI()jKMs8n9m0m zxYa>6L^|0JcaWP=g*sHB4ppebV7a5O4Z-aPTwEZIp3{Wzs9kv9ZC?{&(X-U__f3f7 z61C)pPaP%%^e`S3kC_l-i?|7~fv#MkOc6|oZ@ef^Hvxb_%!K&Yd|vP+IK`>p(sP&) zYoaCudg^0BV9CeB%P =0Lr-U^D9aWaGk*tYkWa-w#RzzdgME&kMq@xA}w=d}hcg0v~@xGzbW4O$*l;NRN zl+p(NMP~%wJmXdCS^)Frnje~t0R1}g#=r #Bf#tOHX~pzzNm)m0M{W2Fy#V_ULi8D3gIE4jVQEC zLq;7!)isnaAmB%q>cWj1ltCOfU2yq@xa80deobNvT(J61$N|B0$qbU{-%3*PZFWmv zv~AC$rcNG}gdF*Q%}e$z7qv))Kd=7NCbWi&h?B3SD+m{0g9S^3-qXaT8P6pUBZ{fL zrP`Mk0WOko3SPuy7hQwQL47npV@lLe3 6jtu-GX~7Tq^_ zUcYZbJT{ViIWS8k6;F_3meAHEM?Zvt U#e{3anOu99HW2|;%4Knb?fl!l4n&Nv?|9t_?=sO{v` zgj{6O@%A0!dR-1)Ne5K5N5JuD-pWW`RCX!iT(XGXj!mcDa92d+n5=5mT*Q$eFGZGD z{VOTQ0(E>r_Gh`T;E!W?)PK1H>!7!=G(yh>mRr zTQSR};F5H_{}o4zWQ%P@ zP5-8JSg{WxMtLrJYG8z(U9V>=x8W~bgxd?a{K?Dkb}a1b@7;o2Z)qs%B!DYH+prM^ z+2p7>fOp}Z-IcJyg6_yf6}Euv(rq&Apy|~|-( %XB4jt7sNGE>FZ)7@i~genhUeYK6#H^1i^ACVw0@6@rIadgUe;~0!9(s z8U^b6g(^;V{t1znu!jB=gleyl2M8>2dd1 >byWWN1hmQWYGGm z4A;JVL#&B8Ui=LmG=SxwTFD)=h7`txNa>rX^iVAu3Ipdy `+$H^ndIl! zXpKCZP{Sfh`*f&yl#ZGuJeyuAWGW0O7~vizU)zLj(OJ*=hY@L4!4HX*Rbpb1a>f=Z z{b3M1l0+}_Cx#nue+Iu9cRooL2Nt_$GwRTU6nK%Ve(0YovwJ+j8x1c1&Qxe6!yO|@ zk7Ut8%%29<7UH=pj$&w#N8L=Ci}JSMZdIoRtMmZpmR!1D8G6ZC7sg+(sLa@?42c** zTXOw}aGwgfV~{&ZNkd^hk3*C61dz2gtwqy76|c=N5wQ&0kFG_GURZS@s%Oa-*Nc!! z7MekCquMB^&Rj|$OkNhc!1W?j9_|XEg*0UZ6<2Usl!}>VE>$efEw|xty3JUq{+vk2 zK0-U}lV9W5L!2)WO?bl9G}qPBVggaNmtOOCWrZBBlPw7-FJBg%{m9G_8}tlE$iz#u z?sw%LtR3M$gm>h9={p}kiYfP%^<(^pO!R0YoG~GTvuBS*8HuFFR`6bSPiTe9_UEq? zzKpQ#F65h(@%S|;Zgr2p( fDC81K{Ng6y~_-Z&b z1OVepD2y?AU~fGxw1n^ElH>_2XOt%!PretG6j(jFk%AiwW1|x1%prFkH+SMB2`k@5 zvcK!lIIUeZ ;{CP_XQJ_HGha)9C01$cD2ZuaCYiK3}_iA|GP#+Kc z?AK;Q;s?VMU&7HD-q(PoD?IT&zQl?39cxPg-$u%V{Ri+|Y)fFwRREv~EQz?payd_& z`c6sJQfN6*tr-FmRxBy4dNE8l*a$N1Jn+OG{049 7ukTj`EsYqGyL)%SVV&rr5!R|4Kuf~SE~>tNr_ zvvkpQ#hzWMxZf8ACWhAa>`IG=m_j$ivW=m6)JiJv3yriVh9*%9F@PyFk{XBsjG+N~ zX;Zsxo23D7Ev`G&sif3xG~gHpi KZdYkpg` W$q$=2gCZfeTjL`>S!)maJ*(T0EEoP0AHHEs*Y@GXXcyV8ddaqPlp0EJ;AX+%*e< zf49UfOvrO4NO+gS@XAd*-Gx_f;>9E>LR?<_UdBJs7^=svbYXVH{HbU!%^Mh+K}(LM zkRDRN(Y|+5N1<)j>*Zc`^hDBLg}JZeWm&HJ4_J3$@L{^`;M$u&P^BA}Br<5X1!wN= zv^JFepU?kQ4p@FGx*Ms77wO?lJzS%QvL0^H!-w?n4|=#w5BKWfQ9b-b55LpH)JH{s z>3TRt58ZmWP!Egsuu2a%>fvTRyiX5*qlZuH;fs2>TMysT!;kc^M-K=7PT-NLhfY1b zUJq;ZaEl&3q=(Pw;VwNqs)zs7!{o !D8%`HS9A^*%lQm>#z1;TwARfgXxp&i>lht9V zU|CB=*uP}=rnAH2%SHY+D- 9`#@5xAR{P4! zr<7Gz@`CsivvS3{a!0w(SM75!W+j6PpT*w!ixvqe2QBi sVJ(R_-VPT$an%)yNLH+EH0uQs$7?lsobrk^U l;ony@{-zeM`?AHTvCdfs1eut1+|V^rKYCZhn#Z!-P~0m zU>%4<8;Gvs7)*)KL-=J|lP5b|F*FpkUAV~OMF&7}m9Kgoy1*aeEvc<7U$?UI7Dt)l ztEgJ-s6tcg2@RC0sH%`FN-8UE<;^GaKipeURYnAtqo!k}vYP&6?=97eFCNz^4v*|W z)9b2hWk+Sj+H$l` Kl}g!By$XNzx3ZEt1E&b5$)(lnYAPzr%N&`PlvesKbgWXU zN;ywCo+UA)s%mAQQYwRZ{O6hO%F4o$iYiV!(LI&uraviMURABEUgM~(DJccLD2_iJ zTT
(5sw3my)Tew*8TV4aCtE)=Or~K#qi0>zh`pHLYgk!9eGAaZ7r{++; zQdP1Ns6u*TB^+zYOKL<*RPKr&pmRe-Ttz;1v3i%EvtF%S_5<}Uqk1(8ByN3$PgY7Q zV}nGa$vYstFfY!*{}t6$<(Qq9KzI;1WFj$&*QL{`UX1SQn(``|3$@kYLs|Lyic-BS z%~$kusm`bJjWw7*kR0MpY=%-PQI7LTLC<=*br8V{d5y0evl0@qHogwEdv5;dmFQ0u z3$#Ti__YdAh?}ll8TiU?R?2G$Ig &bLobOS5fM#uB~1rJFcnV)aFl`6y=LoC`DpwGUmdRDO1?Y$X^MsDuLEks@7Ij zZ>YKw w#vCQWJ#>m}rg_Vl^vS zT|TRNqo}!LQNh9(74Taa6HMmBB+G#0lvL|LTiT$(Lx$Ri4No62a@6QCW5+qhPdG1Q z;`tY3UU<=@iziRHB 1aNhi)1q&A~ z#uIx>ue<(+Wy^0|QCzaJw5)v9>NOQNt*u;FRb6wluU1ypZ`gRtt+(C2=?>bU{lEQj zu>Sut@%P)szy3LAOG~)9{X_p6*!TbSEnm-K{|xLu`xoEBW)}NrWdGT}NPWlu+k?-k z|Nf}1`M)gzhWhBg$c^@Y7e+>q!p(-*KcnG4`xmMIed_;LA^&a$P)|egFVZ8WV(~Qn zj@G}UHNoov8t=NhDX>MoCwT9Vnty!XPws!zThi``otYx9`~b!i#@>>E)Kz zU2VJf>}}t-|CIv=554+Y$KfNdzwzc ;vhno1>h*on z^RWY@>_z1@*k+4@E@ib+3*&5#Qpp>i1Dl^JyJ~zDwEHhC@s+OOtuc0W`AVOH4Y;0U zb4sds68$pt#n3*r&Cn*bhi?_?-$HFsJ5- -;deW1*~%c{;g-f zUdTEZ;@^GjH;Y*3BK-Ra)4hE$L`E_C#|)RU@dVsBl{xX6N_I;&z0{boTc@!M3U9lN zO{DPl%h`==EFPS`f}Kz4J95}X6y7- nF1nY#d{o^VlRx`z_4EV6_g! zF|Cc=&ulld1q` Abc84fuHm9qk7W^AyJEo6fk8zQsCSZ~ rEEn#-X zhE*^RLmR`Z*?cs_(rZ`|RwXv#26h|KF>*QEM31 {@_|3HlJXX H%GQA`*RwKK1$1t}bHCLUT<&G%8?h&?Dydvn>nmL*oNmi* zf>W$~*~ZJKEn8U5y@3KUrKW7 14r1MC*$B{O>&%1lFi;h>hSU>nib zh)FUJ6Qc&-h4>n5`1>Hh$2K*kW>C6?rI)3!^l8Z~ec{lK!7XVuk$Slh%8&5P!sMb4 z;ogaO3553khmbZKfk$#5m;tAg)@iBCx^O^CQcYqp)p0=B7vlRO(Tn$G43ZO>dD?(J za08z9vdkjffLECX_$6`t41+CfF!~sb@`I-iY)P$2DIPQ;nc-eK8&QT3@DWq(okKce z^xiRnvB&Z4z?VLQhM3q8v^Qj`jnifX>}klud-xV{mBrXsh+l+wjh-G)GjE!SbtCPA z8k2sd)7u}0w$d)ZWW_hWOgzjm)WU{>rlE_I*ii6&=+v|tLs9}unw3ywgh&q W05=03Dc2{T kEBpO-bLSu_zY$_Xz{ISR%i+0AMov~9# zcBXd>YZ2p=f^kY_wuLENu8S~jcj0@G`X
qbccHURbWl+#i8*h*t(pX_W^nr2N> zhQz7H8bdnd9dxCGu5`fCrwZ90#OD#_59XL1V`ZNjldD-Xuz2D7G`{pP3`k%D49_we zL&NX^Y F)v4;pZ>DCFb`#*#+_W1(@jPLGtG*P z`D`rvGQM?s*gt;{;QR5VdCo&Ic144RCj*}};9_ILeM7njb=o?tEh*8teY1 i9zRpaaUrQUDZU;4mCWF$d%JO|zJEE{Zinx$YoI*KvJ&EPrL$w^!%NBX=Mw113m z+5+kkeU40KBjo{XB v8S#DK1)%F>T#ScEO{ZVBbZ;1QIdQ5 zTeCiVFD4yDW%TtS9v-cWHOP=;WJ!jHG3FFL$M{%44h#d0Y~a-7EJL!9_>wi^QtM^s z$!RczY-|+f;PC9hzJc9*3|kVxrwBd7kEd7R5o>(^MmQi%@%Qn4k 4Zg*Xh&07BsxelS<~R)D`B50hVIXj@0f(W$VQ`-|47k6O@=sG1wOv$$ zes0Dq+^H?nEwZf-Oz?LQOMWJ?nDZ0swjtHXQVq~ysNY=6*n{}?>bOz-Ieh;XX%G6a z56lkT1KuRdsnE|Ued=@h@fov5;GIb{hdPQGx2#88kWw%re<*sicBga>=tyeevX>R% z6G&j=Z?dqP7TcIzMyawitPKNMGi mDSvaH~izv z@#pJ?6?pZ9_n*TzZ^C8)-)4O2!}+oqHpEt* Q`b$T%ARwxQ$RXOIfw5+(*YjaVJvz zBx$fw!YQV!66cc4!|N%y9#ADO=Es$uGRD4OS69}ei)%_M;Tsqt+FrK~y?e2BaItjW z!qw;o7t1L3nsQ$)4zLPva`Zb&>vtolAI5$Yfw;=ba5xVwQBM$%rc{;-l3dWNEdEl( z9}=#`*;x72IId@R#!6Fq9!}{{ehkL}NXDIbVe`zP`8ao#f!i>ia^rN=SA7fnoTwsX zIaN0$EUv8OBz#729-BgZrL!`A (AZ}R_J$)3+2isRU2^3Sg?^jVqBn< z`)(;J_whR*xRk(2gJ |va#V&B^Dcod2|VR7V4TW=m055M0hlM|Ly;Q|Hb3h*z5`wp^+PWka{ z5slC}voAAxYTf0yak9GHR|H|jv|t^?jr@`tc;P@f5ZfrTc}6czob|(GNaIA~IZt(A zlMq*m#8jlXD<3QU0~DKIP)PAy^a`o0D9fjVWSqgW(OhyDmJ9J3D`dUmR><_t#^?wx zu8s^VTTiWd`PCC4R_sNB2$> khi&@>JC*vI~wFF&AmbUF>p}Us+wN&v~u{BloT5 zJdX7g5VQVGNBUxXo%oWos}tWkd~5J^;=35|JMeu5-@oHK;0bEZz>M%XnHcwQ@MH5i zCF@I8@Z<_{%3Z5VQ00o%eO2fWm+V(rDz8|*MurFUhg$Df2V*6#`JpOwS%go_A859= zFVaNx|8G7;Bl_OYGwPJ3Hwm~!PiuFJG&}_Svt3WaYrsDaJq n(P&eYRz6^W0Y9;qkyHH!A(i{+nYJq;HT|E$r|WA(J8r^o5(&IVE6 zp{F0$)8qB@(|(aZK~JaYX_&a;qo*+i`KNocD1WP-?$py~^z>0Z9oEzP^mK$@I+)ML zVMx$8&^JL3VMUXFm(NvL)8tDg|1|lM$zM!f93B#k(ljoV2k%KVkJ_cW6vC&?+o%2s zM&m?dLtpBb_&{*DNO&5Gil?z{=jkGZRA-?MU!;dnV7z^7C3)YKI(!{M2fj7<(ir*h zC0vOP!lw>j!ns}#HzTBcKfaXTfG^?mV|@R=_RcOgj_M4 sJh9^iKaiYTN3`8I9Y$XJ zGERT#$lp&sKIY`}Ku7+;j(o8r&-hGR|KX1OBOQ6oFU^w>CA&O_d@^ 7ln@`P{%y!%v)9kimZ`C$)u#Rcb z%)ugitHraw-5IBVKDv-*oC0op@&DVO|M4jipEu_GY`^x$oK1WH@$bIv?j!Afs=jTE z@@B)ko}Orbg!a}3ZPxIAY2)}t*ZOy&{S{*hy)Vp?Xa7zAUt9m@@c(<|-O8cPdzHz` ziq7htuj^l1Zm+SN4|vPRSB|^Rs`2Z`Px_|+j$Lm}d`95KTC?U!mA8MjVS(dpzruZO zHDAbQbJJ3MK0eA`_ku^vdG-#=g5@oidCMCtbC$kk)-q%1S*9)LTlQFXTc#}MT6S6P zXX#k(dDygX%JNgomgR2CNlT)H0$oeSEMK PiE c0drtbarrSBL4uA+;P;!gC8Pd^{0 zSRp<4+lgxItO>}g_qLskJ5v17$LM8z#QhIq|6nI$jud+|XCD98nR0PyH}5O4Gw^!i zh~}1fCIJ74hM(j;AKZ?1(}pqlIZ}J{5MU9K?ZZE#e%1})2S{;2EqKPk#`fR=JTOuF zQ?Tz4wvE5ykS;*lRvx~NRA(|KNwG;=d9W1VFV6cVzSu5|W01uCxtZTWxp)BDgq?y{ zqHWlD_z=?Aw%|M_wCprojkMi7^o}4#3V*TBW!$mT&^gkSr{G0M>-uopQM^ONN3lsC zAoZmM*Bov78^G@Q#&+SdV|cf_fiZ{Ip?2TlAFN%3?;y1&8LOn2r4f{8UGe9~5_gE5 zj7w6?QU?7=^}|chdTbw#p&GWhvX?U2>BE+_#V@!Pj^d+uq(jkGY!_CL)=kC~DJF^E z7IM7D48O#7vBiUR^2B!G;xyaA_TZ8e2`0r(#tA9D=pHWBTPPQc=m~7`h?AL%*e+a& zG=F^fl(j?nyHl9M_#|V66i0OSLY{3sZg>-R7q%FnDf~mYqMv d**nPTbPa1AV0X}2!wMDcUo9|*feT&V0Ov97UU`|r5xS)%X z>hWO#X)Xoud89siXA*picH*x%p)!(x6YfBts(!c!wdb(M@1HH9zswoM|GbI1u~TOe zFM--V@bIO?JW%e!CFor242)xa#C5-6{!lJ%M(eSg@S1bXv7CqN2aH{Wrz|tJ2RB$d zguTBtb0#>~ao#|h&l6C;-m(o_aX_~r^(%l2&Nt_oWDJmEfr1M-FUbe$P0=3K&A_?K z* kjHWz19$>_hE(U>3EHag|@n zaiaR+%}8-M;)kdkTXZj@UVOw?k&YK{C2#CdKRzR?XtQs|%7rf>kMc3N@Jh3-JiPoW z`bW7B|Aw;IO?b}LraS{{t68pc7{}-&V{sIxv*=p<@DV>mC2VmD4Qk!%81I`nr(q}K zY?3iHimO?Nj(ghh)I4(xTU>_bVkhHh6i@RJDo`#iyxDPb*dDz1_Z)NB$+($he2ije zK1ZYYi29DfFt!+=n%0G%ASXn(GH=(g->{SME{c2k#qC@pDRK-bHaQnLBAG zKH@6mWBc%4R8*btuSoNu33sC= KnyvcP zTs2=URD c zXe1hq#-d%(L^K(-qOOM9=xwAM{SB`%(8x5h4Zo3V6dKNm>1UdLsf+v4z7hB-j=)zl C4G91M literal 0 HcmV?d00001 diff --git a/lib/src/src/main/resources/jline/keybindings-mac.properties b/lib/src/src/main/resources/jline/keybindings-mac.properties new file mode 100644 index 0000000..6f13615 --- /dev/null +++ b/lib/src/src/main/resources/jline/keybindings-mac.properties @@ -0,0 +1,62 @@ +# Keybinding mapping for JLine. The format is: +# [key code]: [logical operation] + +# CTRL-B: move to the previous character +2: PREV_CHAR + +# CTRL-G: move to the previous word +7: PREV_WORD + +# CTRL-F: move to the next character +6: NEXT_CHAR + +# CTRL-A: move to the beginning of the line +1: MOVE_TO_BEG + +# CTRL-D: close out the input stream +4: EXIT + +# CTRL-E: move the cursor to the end of the line +5: MOVE_TO_END + +# BACKSPACE, CTRL-H: delete the previous character +# 8 is the ASCII code for backspace and therefor +# deleting the previous character +8: DELETE_PREV_CHAR + +# TAB, CTRL-I: signal that console completion should be attempted +9: COMPLETE + +# CTRL-J, CTRL-M: newline +10: NEWLINE + +# CTRL-K: erase the current line +11: KILL_LINE + +# ENTER: newline +13: NEWLINE + +# CTRL-L: clear screen +12: CLEAR_SCREEN + +# CTRL-N: scroll to the next element in the history buffer +14: NEXT_HISTORY + +# CTRL-P: scroll to the previous element in the history buffer +16: PREV_HISTORY + +# CTRL-R: redraw the current line +18: REDISPLAY + +# CTRL-U: delete all the characters before the cursor position +21: KILL_LINE_PREV + +# CTRL-V: paste the contents of the clipboard (useful for Windows terminal) +22: PASTE + +# CTRL-W: delete the word directly before the cursor +23: DELETE_PREV_WORD + +# DELETE, CTRL-?: delete the previous character +# 127 is the ASCII code for delete +127: DELETE_PREV_CHAR diff --git a/lib/src/src/main/resources/jline/keybindings.properties b/lib/src/src/main/resources/jline/keybindings.properties new file mode 100644 index 0000000..9585e3a --- /dev/null +++ b/lib/src/src/main/resources/jline/keybindings.properties @@ -0,0 +1,68 @@ +# Keybinding mapping for JLine. The format is: +# [key code]: [logical operation] + +# CTRL-A: move to the beginning of the line +1: MOVE_TO_BEG + +# CTRL-B: move to the previous character +2: PREV_CHAR + +# CTRL-D: close out the input stream +4: EXIT + +# CTRL-E: move the cursor to the end of the line +5: MOVE_TO_END + +# CTRL-F: move to the next character +6: NEXT_CHAR + +# CTRL-G: move to the previous word +7: ABORT + +# BACKSPACE, CTRL-H: delete the previous character +# 8 is the ASCII code for backspace and therefor +# deleting the previous character +8: DELETE_PREV_CHAR + +# TAB, CTRL-I: signal that console completion should be attempted +9: COMPLETE + +# CTRL-J, CTRL-M: newline +10: NEWLINE + +# CTRL-K: erase the current line +11: KILL_LINE + +# CTRL-L: clear screen +12: CLEAR_SCREEN + +# ENTER: newline +13: NEWLINE + +# CTRL-N: scroll to the next element in the history buffer +14: NEXT_HISTORY + +# CTRL-P: scroll to the previous element in the history buffer +16: PREV_HISTORY + +# CTRL-R: redraw the current line +18: SEARCH_PREV + +# CTRL-U: delete all the characters before the cursor position +21: KILL_LINE_PREV + +# CTRL-V: paste the contents of the clipboard (useful for Windows terminal) +22: PASTE + +# CTRL-W: delete the word directly before the cursor +23: DELETE_PREV_WORD + +# CTRL-X: temporary location for PREV_WORD to make tests pass +24: PREV_WORD + +# ESCAPE probably not intended this way, but it does the right thing for now +27: REDISPLAY + +# DELETE, CTRL-?: delete the next character +# 127 is the ASCII code for delete +127: DELETE_NEXT_CHAR diff --git a/lib/src/src/main/resources/jline/windowsbindings.properties b/lib/src/src/main/resources/jline/windowsbindings.properties new file mode 100644 index 0000000..d599c69 --- /dev/null +++ b/lib/src/src/main/resources/jline/windowsbindings.properties @@ -0,0 +1,68 @@ +# Keybinding mapping for JLine. The format is: +# [key code]: [logical operation] + +# CTRL-A: move to the beginning of the line +1: MOVE_TO_BEG + +# CTRL-B: move to the previous character +2: PREV_CHAR + +# CTRL-C: toggle overtype mode (frankly, I wasn't sure where to bind this) +3: INSERT + +# CTRL-D: close out the input stream +4: EXIT + +# CTRL-E: move the cursor to the end of the line +5: MOVE_TO_END + +# CTRL-F: move to the next character +6: NEXT_CHAR + +# CTRL-G: move to the previous word +7: ABORT + +# CTRL-H: delete the previous character +8: DELETE_PREV_CHAR + +# TAB, CTRL-I: signal that console completion should be attempted +9: COMPLETE + +# CTRL-J, CTRL-M: newline +10: NEWLINE + +# CTRL-K: Vertical tab - on windows we'll move to the start of the history +11: START_OF_HISTORY + +# CTRL-L: Form feed - on windows, we'll move to the end of the history +12: END_OF_HISTORY + +# ENTER: newline +13: NEWLINE + +# CTRL-N: scroll to the next element in the history buffer +14: NEXT_HISTORY + +# CTRL-P: scroll to the previous element in the history buffer +16: PREV_HISTORY + +# CTRL-R: search backwards in history +18: SEARCH_PREV + +# CTRL-U: delete all the characters before the cursor position +21: KILL_LINE_PREV + +# CTRL-V: paste the contents of the clipboard (useful for Windows terminal) +22: PASTE + +# CTRL-W: delete the word directly before the cursor +23: DELETE_PREV_WORD + +# CTRL-X: temporary location for PREV_WORD to make tests pass +24: PREV_WORD + +# CTRL-[: escape - clear the current line. +27: CLEAR_LINE + +# CTRL-?: delete the previous character +127: DELETE_NEXT_CHAR diff --git a/lib/src/src/site/apt/building.apt b/lib/src/src/site/apt/building.apt new file mode 100644 index 0000000..a091378 --- /dev/null +++ b/lib/src/src/site/apt/building.apt @@ -0,0 +1,39 @@ + ------ + jline + ------ + +Building JLine + + Building JLine requires an installation of {{{http://maven.apache.org/}Maven 2}}. + + Source code is included in the JLine distribution, or can be checked out from CVS as described {{{source-repository.html}here}}. + +Maven Repository + + If you are using Maven 2, you can add JLine as an automatic dependency by adding the following to your project's pom.xml file: + ++--------------------------------+ + + + ... + ++ +jline +JLine Project Repository +http://jline.sourceforge.net/m2repo ++ ... + + ++--------------------------------+ + + + + + diff --git a/lib/src/src/site/apt/downloads.apt b/lib/src/src/site/apt/downloads.apt new file mode 100644 index 0000000..de90db9 --- /dev/null +++ b/lib/src/src/site/apt/downloads.apt @@ -0,0 +1,39 @@ + ------ + jline + ------ + +Download JLine + + JLine packages can be downloaded from: + + {{{http://sourceforge.net/project/showfiles.php?group_id=64033}http://sourceforge.net/project/showfiles.php?group_id=64033}} + + +Maven Repository + + If you are using Maven 2, you can add JLine as an automatic dependency by adding the following to your project's pom.xml file: + ++--------------------------------+ + ++ +jline +jline +0.9.9 ++ ... + ++ +jline +JLine Project Repository +http://jline.sourceforge.net/m2repo ++ ... + + ++--------------------------------+ + + + + diff --git a/lib/src/src/site/docbook/index.xml b/lib/src/src/site/docbook/index.xml new file mode 100644 index 0000000..d884ba3 --- /dev/null +++ b/lib/src/src/site/docbook/index.xml @@ -0,0 +1,608 @@ + + ++ +jline +jline +0.9.9 ++ diff --git a/lib/src/src/site/fml/faq.fml b/lib/src/src/site/fml/faq.fml new file mode 100644 index 0000000..b9902b0 --- /dev/null +++ b/lib/src/src/site/fml/faq.fml @@ -0,0 +1,26 @@ + ++ ++ JLine + ++ +2002, 2003, 2004, 2005, 2006, 2007 +Marc Prud'hommeaux ++ + +JLine Manual + ++ + +Introduction ++ JLine is a Java library for handling console input. + It is similar in functionality to BSD editline and GNU + readline. People familiar with the readline/editline + capabilities for modern shells (such as bash and tcsh) will + find most of the command editing features of JLine to + be familiar. + ++ + +License and Terms of Use ++ JLine is distributed under the BSD license, meaning that + you are completely free to redistribute, modify, or sell it + with almost no restrictins. + For more information on the BSD license, see + +http://www.opensource.org/licenses/bsd-license.php . ++ For information on obtaining the software under another + license, contact the copyright holder: + +mwp1@cornell.edu . ++ + +Obtaining JLine ++ JLine is hosted on SourceForge, and is located at + +http://jline.sf.net . + The latest release can be downloaded from ++ http://sourceforge.net/project/showfiles.php?group_id=64033 . + API documentation can be found in the +apidocs/ + directory. ++ +Installation ++ JLine has no library dependencies, aside from a JVM + of version 1.2 or higher. To install JLine, download the + +jline.jar + file, and either place it in + the system-wide java extensions directory, or + manually add it to your +CLASSPATH . + The extensions directory is dependent on your operating + system. Some few examples are: ++ ++ ++ Macintosh OS X: + +/Library/Java/Extensions + or +/System/Library/Java/Extensions ++ ++ Microsoft Windows: + +JAVAHOME\jre\lib\ext + (example: +C:\j2sdk1.4.1_03\jre\lib\ext ) ++ ++ UNIX Systems: + +JAVAHOME/jre/lib/ext + (example: +/usr/local/java/jre/lib/ext ) ++ JLine is not 100% pure Java. On Windows, it relies on a + +.dll + file to initialize the terminal + to be able to accept unbuffered input. However, + no installation is necessary for this: when initialized, + JLine will dynamically extract the DLL to a temporary + directory and load it. For more details, see the + documentation for the +jline.WindowsTerminal + class. ++ On UNIX systems (including Macintosh OS X), JLine will + execute the + +stty + command to initialize + the terminal to allow unbuffered input. For more details, + see the documentation for the +jline.UnixTerminal + class. ++ For both Windows and UNIX systems, JLine will fail to + initialize if it is run inside a strict security manager + that does not allow the loading of libraries, writing + to the file system, or executing external programs. However, + for most console applications, this is usually not the case. + ++ +Supported Platforms ++ JLine should work on any Windows system, or any + minimally compliant POSIX system (includling Linux and + Macintosh OS X). + ++ The platforms on which JLine has been confirmed to work are: + ++ ++ ++ Microsoft Windows XP + ++ ++ RedHat Linux 9.0 + ++ ++ Debian Linux 3.0 + ++ ++ Macintosh OS X 10.3 + ++ Please report successes or failures to the author: + +mwp1@cornell.edu . ++ +Features ++ +Command History ++ ++ +Tab completion ++ ++ +Line editing ++ ++ +Custom Keybindings ++ You can create your own keybindings by creating a + + +HOME/.jlinebindings.properties" + file. You can override the location of this file with + the "jline.keybindings " + system property. ++ +Character masking ++ ++ +API ++ This section discusses some common usages of the JLine API. + For in-depth usage of the JLine API, see the + +apidocs . ++ +Reading a password from the console ++ A common task that console applications need to do is + read in a password. While it is standard for software + to not echo password strings as they are typed, + the Java core APIs surprisingly do not provide any + means to do this. + ++ JLine can read a password with the following code: + ++ String password = new + This will replace every character types on the console + with a star character. +jline.ConsoleReader ().readLine(new Character('*')); ++ Alternately, you can have it not echo password + character at all: + ++ String password = new +jline.ConsoleReader ().readLine(new Character(0)); ++ The + +jline-demo.jar + file contains + a sample application that reads the password. To run + the sample, execute: ++ java -cp jline-demo.jar jline.example.PasswordReader "*" + ++ +Frequently Asked Questions ++ ++ Can I disable JLine if it isn't working on my platform? + ++ You can disable JLine by setting the System property + " +jline.terminal " + to + "jline.UnsupportedTerminal ". For example: ++ java -Djline.terminal=jline.UnsupportedTerminal jline.example.Example simple + ++ ++ How do I customize the key bindings? + ++ You can create your own keybindings by creating a + +HOME/.jlinebindings.properties" + file. You can override the location of this file with + the "jline.keybindings " + system property. To examine the format to use, see the +src/jline/keybindings.properties + file in the source distribution. ++ ++ Can I use JLine as the default console input stream for + all applications? + ++ No, but you can use the + +jline.ConsoleRunner + application + to set up the system input stream and continue on + the launch another program. For example, to use JLine + as the input handler for the popular +BeanShell + console application, you can run: ++ java + +jline.ConsoleRunner +bsh.Interpreter ++ ++ Can I use JLine as the input handler for +BeanShell ? ++ Yes. Try running: + ++ java + +jline.ConsoleRunner +bsh.Interpreter ++ ++ Can I use JLine as the input handler for + +jdb + (the java debugger)? ++ Yes. Try running: + ++ java + +jline.ConsoleRunner + com.sun.tools.example.debug.tty.TTY +args ++ ++ Is JLine +100% pure Java ? ++ No: JLine uses a couple small native methods in the Windows + platform. On Unix, it is technically pure java, but relies + on the execution of external (non-java) programs. See the + installation section + for more details. + ++ ++ How do I make it so password characters are no echoed + to the screen? + ++ See. + ++ ++ Is JLine a full-featured curses implementation? + ++ No: JLine has no ability to position the cursor on the + console. It might someday evolve into a plausible + Java curses implementation. + ++ + +Known Bugs ++ ++ ++ Clearing the screen (CTRL-L) doesn't currently work on Windows. + ++ + +Contributors ++ The following people have contributed to improving JLine over the + years: + ++ ++ ++ Marc Prud'hommeaux + ++ ++ Damian Steer + ++ ++ Dale Kemp + ++ ++ Jun Liu + ++ ++ malcolm@epcc.ed.ac.uk + ++ ++ Simon Patarin + ++ ++ Amy Isard + ++ ++ Ryan Bell + ++ ++ Marc Herbert + ++ ++ Christian Salm + ++ + +Future enhancements ++ ++ ++ Add localization for all strings. + ++ ++ Create a BNFCompletor that can handle any BNF. + ++ +Change Log ++ +0.9.93 2007-11-13 ++ ++ Fixed backspace handling on Unix/OS X. + ++ +0.9.92 2007-10-30 ++ ++ JLine now works with 64-bit Windows systems. + ++ +0.9.91 2007-03-11 ++ ++ Added ConsoleReader.setUsePagination() method which allows + configuration of pagination when the number of rows of + candidates exceeds the height of the detected terminal, thanks + to a patch by Damian Steer. + ++ ++ Better support for UTF-8 inputs (issue #1623521). + ++ ++ Improved list of supported keys on Windows (issue #1649790). + ++ +0.9.5 2006-03-08 ++ ++ Fixed problem with "stty" on Solaris, which doesn't + understand "stty size" to query the terminal size. It now + uses "stty -a", which supposedly outputs a POSIX standard + format. + ++ ++ Support HOME and END keys, thanks to a patch by + Dale Kemp. + ++ +0.9.1 2005-01-29 ++ ++ Fixed problem with the 0.9.0 distribution that + failed to include the Windows jline.dll in the jline.jar, + rendering it inoperable on Windows. + ++ ++ Implemented proper interception or arrow keys on Windows, + meaning that history can now be navigated with the UP + and DOWN keys, and line editing can take place with + the LEFT and RIGHT arrow keys. + ++ +0.9.0 2005-01-23 ++ ++ Changed license from GPL to BSD. + ++ ++ Made "CTRL-L" map to clearing the screen. + ++ +0.8.1 2003-11-18 ++ ++ Fixed accidental dependency on JVM 1.4. + ++ +0.8.0 2003-11-17 ++ ++ Windows support using a native .dll + ++ ++ A new ClassNameCompletor + ++ ++ Many doc improvements + ++ +0.6.0 2003-07-08 ++ ++ Many bugfixes + ++ ++ Better release system + ++ ++ Automatically set terminal property by + issuing stty on UNIX systems + ++ ++ Additional tab-completion handlers + ++ ++ Tested on Debian Linux and Mac OS 10.2 + ++ ++ Example includes dictionary, filename, and simple completion + ++ +0.3.0 2002-10-05 ++ ++ Initial release + ++ diff --git a/lib/src/src/site/resources/css/site.css b/lib/src/src/site/resources/css/site.css new file mode 100755 index 0000000..771dc3f --- /dev/null +++ b/lib/src/src/site/resources/css/site.css @@ -0,0 +1,311 @@ + +body { + min-width: 600px; + width: 600px; + width: auto !important; + background-color: #fff; + font-family: Verdana, sans; +} + +body,div,span,td,p,h2,h3,h4,h5,h6,a,ul,li { + font-family: Verdana, sans; + font-size: 11px; + color: #5A5A5A; + font-style: normal; +} + +a,a:hover,a:visited,a:active { + color: #5A5A5A; + /* text-decoration: underline; */ +} + +/* main layout */ +#banner { + color: #FFA500; + border: none; + margin: 0 0 0 0; + background-color: #fff; + background-image: url(../images/header.jpg); + background-position: right; + background-repeat: no-repeat; + height: 100px; +} + +#bannerLeft img{ + margin: 10px 0 0 10px; +} + +#leftColumn { + background-color: transparent; + position: absolute; + top: 140px; + left: 20px; + width: 180px; + margin: 0px; + padding: 0px; + border: none; +} + +#bodyColumn { + margin: 0 0 20px 220px; + background-color: #fff; + padding: 30px; + position: relative; + background-image: url(../images/dotted.png); + background-repeat: repeat-y; +} + +#footer div.xright { + color: #fff; + margin-right: 10px; +} + +/* end main layout */ +.deprecated { + text-decoration: line-through; +} + +.comment { + color: green; +} + +.source pre { + font-family: "Andale Mono", monospace; + font-size: 11px; + background-color: #ddd; + width: 100%; + color: #5A5A5A; + border-width: 0px; + padding-top: 6px; + padding-left: 3px; +} + +#breadcrumbs { + background-color: #FE1100; + border: none; + height: 15px; +} + +/* + workaround for bug in the Doxia docbook renderer that opens + anchors (e.g. ), but doesn't ever close them +*/ +#section a,a:hover,a:visited,a:active { + text-decoration: none; +} + + +#breadcrumbs a { + color: #fff; + margin-left: 20px; + text-decoration: none; +} + +h1 { + border: none; + padding-left: 0; + font-weight: bold; + text-transform: capitalize; + background-color: #7FAABB !important; + color: #FFFFFF !important; + font-size: 19px !important; +} + +h2 { + border: none; + padding-left: 0; + font-size: 13px; + font-weight: bold; + text-transform: capitalize; + background-color: #7FAABB !important; + color: #FFFFFF !important; + font-size: 17px !important; +} + +h3 { + border: none; + font-weight: bolder; + padding-left: 0; + background-color: #8BBBD1 !important; + color: #FFFFFF !important; +} + +#navcolumn { + padding: 0; + overflow: hidden; +} + +#navcolumn ul { + margin: 0px 0 3px 0; + background-repeat: repeat-x; +} + +#navcolumn h5 { + border: none; + background-image: url(../images/dotted.png); + background-repeat: repeat-x; + padding: 4px 0 3px 20px; + font-size: 11px !important; + margin-top: -1px; +} + +#navcolumn ul { + margin-bottom: 8px; +} + +#navcolumn li { + margin: 0px 0 0px 3px; + padding: 2px; + list-style-position: outside; + font-size: 7.5pt !important; + padding-left: 16px; + padding-left /**/: 2px !important; + /* warning, don't reformat, there should be no comment between padding-left and comment, to fix IE5 issues */ +} + +#menuDownloadable_docs li { + background-image: url(../images/ico_file_pdf.png); + padding-top: 3px; + padding-bottom: 1px; +} + +#navcolumn strong { + color: #000000; + font-weight: bold; +} + +#navcolumn strong a { + color: #000000; + font-weight: bold; +} + +#navcolumn a { + padding-left: 14px; + text-decoration: underline; + padding-bottom: 2px; + color: #5a5a5a; +} + +#navcolumn a img { + margin-top: 0; +} + +#navcolumn a#poweredBy img { + margin: 0 0 15px 20px; + width: 90px; + height: 30px; +} + +#navcolumn #lastPublished { + color: #999; + margin: 0 0 0 20px; +} + +#navcolumn a:hover { + color: Olive; + padding-left: 14px; + text-decoration: underline; + padding-bottom: 2px; +} + +#breadcrumbs div.xright,#breadcrumbs div.xleft { + color: #fff; + display: inline; + font-size: 7pt !important; +} + +#banner a#projectLogo img { + float: left; + background-color: #fff !important; + margin: 20px 0 0 20px !important; +} + +#navcolumn li { + color: #000000; +} + +#navcolumn strong { + color: #000000; + font-weight: bold; + margin-left: 15px; +} + +div.source { + background-color: #ddd; +} + +div.source pre,code,td.code { + font-size: 8pt !important; + font-family: monospace; + margin: 0; +} + +td.code { + font-size: 10pt !important; + font-family: monospace; +} + +div#legend { + display: none; +} + +table td.source { + border: none !important; +} + +table td,table th { + font-size: 8pt !important; + font-family: verdana; +} + +table th { + font-weight: bold; +} + +.collapsed { + background-image: url(../images/collapsed.png) !important; +} + +li.expanded { + background-image: url(../images/expanded.png) !important; +} + +/* +li.expanded ul { + margin-top: 5px !important; +} +*/ + +a.externalLink,a.newWindow { + padding-right: 9px !important; + background-image: none !important; /*ie5*/ +} + +a.externalLink /* */ { + background-image: url(../images/external.png) !important; +} + +a.newWindow /* */ { + background-image: url(../images/newwindow.png) !important; +} + +table { + width: 68%; /* fix for ie 5.x */ +} + +i { + content: "\"/*" +} + +table { + width: 100%; +} +/* remove banner: comment the following lines for the full layout */ /* +#banner, #breadcrumbs { + display: none !important; +} +#leftColumn { + position: relative; + top: 0; +} +*/ diff --git a/lib/src/src/site/resources/images/collapsed.png b/lib/src/src/site/resources/images/collapsed.png new file mode 100755 index 0000000000000000000000000000000000000000..a02c1e67c9d56d299c6116d02edf96cf547aee41 GIT binary patch literal 222 zcmeAS@N?(olHy`uVBq!ia0vp^+#t-s%)r3-vUGbBki%Z$>FdgVg`16=SMyEh+ ++ +How does JLine work? ++ ++ On Windows, JLine uses a native .dll (which it automatically + extracts from the jline jar and loads at runtime) to access + native console features. On UNIX systems, JLine will perform + the necessary terminal setup by launching the "stty" command. +
++ + +What platforms has JLine been tested on? ++ ++ Various flavors of Windows (95, 98, NT, XP, 2000), Mac OS X, + and multiple UNIX systems (Linux, Solaris, HPUX). +
+0By_5c6>4<9~Uym O@y(mp+_-sT_3Fi?n-0hWl`)nC`2{mLJiCzw %Zg#l`uV{olTRW)@Flh+T613mec7 N22WQ%mvv4FO#r}6PN@I@ literal 0 HcmV?d00001 diff --git a/lib/src/src/site/resources/images/dotted.png b/lib/src/src/site/resources/images/dotted.png new file mode 100755 index 0000000000000000000000000000000000000000..8a4d443a4a1ddc94e4d26dd2f5444c53860f658c GIT binary patch literal 190 zcmeAS@N?(olHy`uVBq!ia0vp^tRT$6%)r2ScH1*YAcrO0(HBUsKVi<=^%BUJEOCt} z3C>R|DNig)WpGT%PfAtr%uP&B4N6T+sVqF1Y6Dcn8Q>G*T346*|Nnm=PeR6YBamV$ z3Gxg6&+v4+fftac>*?YcQgJIOfq_GT&8>kUDItXg!~(IIf_fMjS<;#W92y!e64(TQ Yu9?Fid8Ph38&D&Ir>mdKI;Vst020bFHUIzs literal 0 HcmV?d00001 diff --git a/lib/src/src/site/resources/images/expanded.png b/lib/src/src/site/resources/images/expanded.png new file mode 100755 index 0000000000000000000000000000000000000000..8a19dbf1038200fe53658868f8e8350220f5147b GIT binary patch literal 198 zcmeAS@N?(olHy`uVBq!ia0vp^+#t-s%)r3-vUGbBki%Z$>FdgVg`16=S6 O>_3{)lr!i<;h*8KqrvZOouIx;Y9?C1WI$O`211o(uw{{R0!?ToSUOa|jL1`XB? zaX@j#k|4ie28U-i(tsQTPZ!6Kid)GE4oqhfCUKrhPGMsbXz%P~ =QlemFU|^zPU} ?NMQuIyL1*|@o_>~c%b z0EIXUJR*x37`TN%nDNrxx<5ccmUKs7M+SzC{oH>NS%G}E0G|-o|Ns9-^)XC*dG;<) zgs~*ZFPOpM*^M+HN5a#^F{I*FazaW%0z)F3##xS3gQS22pej=a?v|8wZ-HtUJYD@< J);T3K0RY|TJLmua literal 0 HcmV?d00001 diff --git a/lib/src/src/site/resources/images/ico_file_pdf.png b/lib/src/src/site/resources/images/ico_file_pdf.png new file mode 100644 index 0000000000000000000000000000000000000000..9ceb00f2dd27d8467114e3cca9477ee5a26ba0bb GIT binary patch literal 280 zcmeAS@N?(olHy`uVBq!ia0vp^d>}RpGXn#o%-5n@Kn{C}r>`sfWiDwxOTz;~Yo-8& zI14-?iy0W?4uLRZ-i1;-pdd@Sqpu?a!^VE@KZ&eBzFdG$h^xgrhGRfB1H(B9iJq-n zv$M0$-Mjby|NnDdUdM`yzaNU~0?IL#1o;IsI6S+N2ILfYx;TbZ+)7SJeUQSDs*%DG zl$a2dl)#%1pp}qdl8_LTz`@H~o{*rC&n%Rb>7ZmHq7t_5z=9 B%$$kS zP@y}})3dR$ajOhRmjn|lE9=$Ma~%F0`etHqH<57}w`vb>tAxZ1*O-`?ISj8YMW6Al RU)ct7rKhW(%Q~loCIEb~SPcLG literal 0 HcmV?d00001 diff --git a/lib/src/src/site/resources/images/logo.jpg b/lib/src/src/site/resources/images/logo.jpg new file mode 100644 index 0000000000000000000000000000000000000000..1f1da5c8f270f3caedcad4d7898dac192f19fd33 GIT binary patch literal 4121 zcmeHJcTm$!xBi9RAp)UG2}OGEqV!Oth9*)}kRm-u6P2QXfK;iWNi`rf^rA=;4B!i) zhh9WM6r`6xK78+d@67$?&fK~G-?KA2vvZ!^=Q(HR>>k22VF_R`(AC!kKp+sHcR2vU zET9FDk&u#+l8}*+l97{>QBW~ZQBhJ-G11Y}Ft9MOva&EiAnaTM-0U1sP6&iYj0Y+x zBrGD##w{)-eoac?n((zhhk(e*$*Cx)7^$cjudzeeul=8m@DTu00A|1jF^CT!0)vRb zAVM#|2>`?Z@gKSW7&!$oDH(`}zh>0o4$S8?P{~$mB5itpvl cL2?p{P4lR3h_s|NyAP<#|wh+fvL-_a`W9>xCQxZFf^$?*>Z09 MfVcp*BG<$>xj!e=|5P-e zF_mLg4^uU(sB$XngG#AULLfw~ON8;dzhQdt 5^>eNk=gIrzT}m`YM6-)sxWYe z8J6h+8jLEZSj>-J)c$|Z|DJ={xIhDmej>EJ&>=L@$UeH?1RCo}3#J$Rg=c)cF=(GV zVtoUthJnFCr&IRzo|#?DM1~lhxXa~s6rb0V?B?uZ-L0f0V}pW{?UVQ~;Pk&Pj5Qge zNNtj%&Sat;dj0M1^qR8@<5osECcSV5uI8Ti)}@@wa|=3zD}!%UyYYvYrz?BGXrdLQ zzcgPBS4~Z)Y=+mhLhngi7|_3;Iw}3+{&BOGeO33HjQ(JN0s)AdQs59%=Xm~<%8EO^ zA)s_vT%3oo#8I6^<-?#=)YJpDoE4=xab#Kfl4@Q4OHLLiNZ&cPK~d;Ax|xR0(B0Q? zZLm$ ?d5fJ6f_x!^Ko^7hZjBIpdy>yJIc2Pd!)| zyz`c3G{_|ErjZ)n?dRRSFT_aqhHWt_tgX0A>g`RpBh+v04g0Jv1k%c?M+clAbi2@1 zbIbQ>_G;s(f_|CUhco%e#R-o+WAmy(uY`#tdZ+mk4x7a)!yG!KAWPdMB^RCL$mBRx za8HCvHE9#$TI(7w6;VAeg(ky`|32mB7ChCz_`N81toO|1LkR&0-bOwVEpbc^lq~ph zgU@9A2^N|b)^s-?{>;`$Nb(v1@a(!?hHpSx<{GZp$pKe7>31bJqDf`30|rmB7f_ zlgD<{Y_VGbZ&Gj$3&ZCX6(2=8>|^?*noA^#WgcaKrw{Snm52Vk&p^id1VGQ~mcZAM zWaFaN8+X(P-r?4%UF8^O{Y#(zlw{5!{oIoN0NP+7qbA+9Q0_ or!@fup0Kba6rgcGF6ICnC^ENUN!7vzq2G-tV50LY^m6y7X zL|!*)V8nduJ%f;Q%y#ZrM2WsgZk%7%Q|6pi7ztBcdY3CZy?Vwg*E45tj19qK%D!sf zif2ksiKk_6#Zj5=#`cN0Z}_&chZJzi)1_b=qjCEiwQ8$a)l>oH5ZIXu 7clzUviWg}K|ILY7q^YBH=~>Sskuy7R&>v*1gFsH5}@Z;3-yt!rx9#7R;IYS z KQWOndoT@!x)+^?m~v3oUng=^EI3X hQc)Pmz*CAim;qn Q<#41jt?*2+ohirM(vAA5CWce5hQGUE&*7X7>Bby)m;ajX9Q4!;W znCnU800rMtq}u0JYpv9sTlZSjj8Bub9M;iWPD!SI1R&$*9Z#5xjSH{dM(q#j$vK ^du)VI!!#t*Lw51W( z-2oqm!5Lj%&R1ip5QwJ-LTCL?#+G?cZk)b8U;W^|Q*HK4W4cA$wO9W%38{@pIvire z_zdcoeErJ(GfOCKG2^vCee4s%_KwIOGeWyrVL`hH{C0&8mIMsr9}7fFhlbQleiwn> zeYC&ZOF#9{X-iZLZY_ThaEm_WmxSVW+e6o_`j}>sl`B~srxih+zKg9TWvUabaYuM7 zOO1>Gm!_^_|Myk>97z@*p7N zt74C$eo&Aj)ZQ7}E3r?1RM85Xv7K*yw*8x4sdN5eR=Pkl?vqAp0rATTGdaJ85H82# z@ `G1KpwzcIheW>794cK5 z82N5T`-Ej%R92oeEH)oELQnoF$N)rQ)cUQfcj= ~gpl1dKC0WS?Kwy4B`8XOG2 cl2CgO*ZX7v!ZT|CD9;JQ1_rPgmfVa;Y+v(aKFlaC(Bv@0S2 zF5kmm`5dYRu~C2{;Li^ -@L!PI9@+c~K&HI(KLdAFuk} zq?@o_yRLqpRZhmn$IYC7eWVkEDVu@n4W=EP7HW1=&dn*0;oCiQER2cuy$dR)y s#+tzFH>4jZ(o{$E66R%yJvJP)L9d20V9DXzAZU$Qov`HKKB?8To? zmM1Dg{vvHV`GoK8v#arP_9E9!D1H2KgC~=yBIPG(kE+Sl=tzZks$}n&8>BHe$0gEp ziwfO7q6C^t_utH(#d8yY(nV((V^Wi_3Km+7 M8jL~4}B#GS@?* skJmfxlqK>OOnZZR6}c9_wexlap|RjXRdA>pW;&00-`9aWaQPr5IJxoz3MG8_;f^ z JcxR(AI#GAey_4_m?u(+b#Ei&Nr1tv#F7L^V)G!pKU{mnI z-QGQtl)<>k8>OR>Y$&qm#TI~QQos@&>mork-!?HOgv!%VAIRPL>9REEeqBdd`QLS% zjQ{uFYn9c>vt{?Neue)WHZ0ooS6YQEwcDm}>By!j0npMp*c_XEx=?eVFZ7ir01b%~ zjX&5V08}4R1qZ}-C~Q=26m MVq>a^+sXkudFtQ;-YQy9u{m6(ELTB5vQiiYGq{W(HCepml=E$8VDxV72^X|J-#Y~R+H=N{p?^Kn z?cq;SRpU56=3lFmD?d)?O0u7s_TS<4aI0252#azUq~?c&YS=vfTQtOg^}j^_HLqBT z5z|fOrMYsu*mT%uh9}T5`Q4kos6d`sRTQ;1$TV$heZ?a*a#tkyQY_Kn1A$jGZXP<} z25<8XVrkGhEk8sHhEJXqaQ7^lz&RYoI>@P~k4ib)*`3pMclC!IC&oPsGIWz`o!k|n z27@9l3DFAIqw{%;KE%Jmgxtsk6^&8@3cn1M1AQ#!`oy*OkeP$BrT!=HiPq+RwVIrX zJsFgD)NAA7aL~$?OE4Ul_t)(2=cwd y9AyZ^sj7q`JHg z(=Q2V>_J71hj;L;^gZ_1ib^c4Q08XrP00b5FpKEdSK8dCed`~fc~xRZ+?T2dZN=(4 zB* N||GhNK7NwK#ej-5V$L?)cq@a2NHxvx*24i0R^3;P=Hs z?)Gk$FFh4AspqbA8$%NSonx5^wxS-smkB@JsC62)nq(z_O^=L$DN3j>3wHVG;c?1u z`(y}&;xdYtZPSP34UM?C1e(7hbqB%q5iKHK7&aKB+n*Bw{w_D2yikkEy=gsa!#(xj zP?(Qy;>~3sw6(R=LuLws>r!raJVafp(uS4WleofnL5PsfYvxX)U{!XL2i;2>O{&_s zYJ>)gxb1ALYyf?Plni*Iw=*~ReGPu`WG1(+_4U0(Hz&5YNbo`a+2W4jSeoC0zsX3m zV~zE(<$f(%!?l4qrg#G{xMSvA7ztx(Z8K3e7Dgw5OJi$$1d#I7isvt?rx)zXyEf(? z+Sq=(vijR7M>4C>S#!vZS|IHW0HKs!a9BC=5FO{G7RCH$!~7rp^Z&S%B+UE^sRomE literal 0 HcmV?d00001 diff --git a/lib/src/src/site/resources/images/newwindow.png b/lib/src/src/site/resources/images/newwindow.png new file mode 100755 index 0000000000000000000000000000000000000000..1374c228afd7fbf0f75bbaf43c98cd8597f80187 GIT binary patch literal 224 zcmeAS@N?(olHy`uVBq!ia0vp^Y#_|R%)r37Kve$?kfUAV8d2h0l$uzQnV+W+l9`*z zP@I^gV5E>=QlemFU|^zPU} zfI^%F9+AZi4BSE>%y{W;-5;PJOS+@4BLl<6e(pbstUx|nfKQ0)|NsA^`WPm@JbM=? z!dMdI7tG-B>_!@pBkAel7*cU7IU$9?$$)W*grtOofP^cT7b62xB7@|1hX^mAA_h-a KKbLh*2~7ZExj4}P literal 0 HcmV?d00001 diff --git a/lib/src/src/site/site.xml b/lib/src/src/site/site.xml new file mode 100644 index 0000000..3301d0e --- /dev/null +++ b/lib/src/src/site/site.xml @@ -0,0 +1,40 @@ + + + From 8ca6d27fb3a8698a0919c1a8b29fed2c1adf7825 Mon Sep 17 00:00:00 2001 From: Viktor Kuchyn+ +JLine +images/logo.jpg +http://jline.sourceforge.net/ ++ + + + + + + + + ${reports} + + +SourceForge +http://sourceforge.net/sflogo.php?group_id=64033 +http://sourceforge.net/ +Date: Mon, 17 Nov 2014 23:48:46 +0800 Subject: [PATCH 16/39] Implemented custom console reader --- .../study/sqlcmd/io/SqlConsoleReader.java | 90 +++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 src/main/java/ua/com/juja/study/sqlcmd/io/SqlConsoleReader.java diff --git a/src/main/java/ua/com/juja/study/sqlcmd/io/SqlConsoleReader.java b/src/main/java/ua/com/juja/study/sqlcmd/io/SqlConsoleReader.java new file mode 100644 index 0000000..de04008 --- /dev/null +++ b/src/main/java/ua/com/juja/study/sqlcmd/io/SqlConsoleReader.java @@ -0,0 +1,90 @@ +package ua.com.juja.study.sqlcmd.io; + +import jline.ConsoleReader; +import jline.CursorBuffer; + +import java.io.*; + +/** + * Created with IntelliJ IDEA. + * User: viktor + * Date: 11/17/14 + * Time: 9:42 PM + */ +public class SqlConsoleReader { + + public static final int SEMICOLON = 59; + public static final int NEW_LINE = 10; + private ConsoleReader consoleReader; + private Writer writer; + private CursorBuffer cursorBuffer; + + public SqlConsoleReader(InputStream inputStream, OutputStream out) throws IOException { + writer = new PrintWriter(out); + consoleReader = new ConsoleReader(inputStream, writer); + cursorBuffer = consoleReader.getCursorBuffer(); + } + + public String readQuery() throws IOException { + StringBuilder sb = new StringBuilder(); + boolean exit = false; + int previousKey = 0; + while (!exit) { + int key = consoleReader.readVirtualKey(); + switch (key) { + case NEW_LINE: + if (previousKey == SEMICOLON) { + exit = true; + } else { + sb.append(" "); + newLine(); + } + break; + case 127: + consoleReader.backspace(); + + break; + default: + sb.appendCodePoint(key); + cursorBuffer.write((char) key); + writer.write(key); + } + previousKey = key; + consoleReader.flushConsole(); + } + return sb.toString(); + } + + private void newLine() throws IOException { + consoleReader.moveCursor(0); + consoleReader.printNewline(); + cursorBuffer.getBuffer().setLength(0); + cursorBuffer.cursor = 0; + + } + +// private class ConsoleWriter extends PrintWriter{ +// private StringBuilder input = new StringBuilder(); +// public ConsoleWriter(Writer out) { +// super(out); +// } +// +// @Override +// public void write(int codePoint) { +// super.write(codePoint); +// input.appendCodePoint(codePoint); +// } +// +// @Override +// public void write(char[] buf) { +// super.write(buf); +// input.append(buf); +// } +// +// public String getInputAndClear(){ +// String res = input.toString(); +// input = new StringBuilder(); +// return res; +// } +// } +} From ecbe5ef4d11381d9c320a234df8d7303b98f329b Mon Sep 17 00:00:00 2001 From: Viktor Kuchyn Date: Tue, 18 Nov 2014 01:05:15 +0800 Subject: [PATCH 17/39] Left, right navigation --- .../study/sqlcmd/io/SqlConsoleReader.java | 39 +++++-------------- 1 file changed, 10 insertions(+), 29 deletions(-) diff --git a/src/main/java/ua/com/juja/study/sqlcmd/io/SqlConsoleReader.java b/src/main/java/ua/com/juja/study/sqlcmd/io/SqlConsoleReader.java index de04008..0700d73 100644 --- a/src/main/java/ua/com/juja/study/sqlcmd/io/SqlConsoleReader.java +++ b/src/main/java/ua/com/juja/study/sqlcmd/io/SqlConsoleReader.java @@ -15,6 +15,7 @@ public class SqlConsoleReader { public static final int SEMICOLON = 59; public static final int NEW_LINE = 10; + public static final int BACKSPACE = 127; private ConsoleReader consoleReader; private Writer writer; private CursorBuffer cursorBuffer; @@ -33,6 +34,7 @@ public String readQuery() throws IOException { int key = consoleReader.readVirtualKey(); switch (key) { case NEW_LINE: + sb.append(cursorBuffer.toString()); if (previousKey == SEMICOLON) { exit = true; } else { @@ -40,12 +42,16 @@ public String readQuery() throws IOException { newLine(); } break; - case 127: + case BACKSPACE: consoleReader.backspace(); - + break; + case 2: + consoleReader.moveCursor(-1); + break; + case 6: + consoleReader.moveCursor(1); break; default: - sb.appendCodePoint(key); cursorBuffer.write((char) key); writer.write(key); } @@ -58,33 +64,8 @@ public String readQuery() throws IOException { private void newLine() throws IOException { consoleReader.moveCursor(0); consoleReader.printNewline(); - cursorBuffer.getBuffer().setLength(0); + cursorBuffer.clearBuffer(); cursorBuffer.cursor = 0; - } -// private class ConsoleWriter extends PrintWriter{ -// private StringBuilder input = new StringBuilder(); -// public ConsoleWriter(Writer out) { -// super(out); -// } -// -// @Override -// public void write(int codePoint) { -// super.write(codePoint); -// input.appendCodePoint(codePoint); -// } -// -// @Override -// public void write(char[] buf) { -// super.write(buf); -// input.append(buf); -// } -// -// public String getInputAndClear(){ -// String res = input.toString(); -// input = new StringBuilder(); -// return res; -// } -// } } From 255aa9deaf465bf9ee4d3d9ffe176aee783225e6 Mon Sep 17 00:00:00 2001 From: Viktor Kuchyn Date: Tue, 18 Nov 2014 12:22:49 +0800 Subject: [PATCH 18/39] Move end, begin --- .../juja/study/sqlcmd/engine/JLineDemo.java | 46 +++++++++++++++++++ .../study/sqlcmd/io/SqlConsoleReader.java | 20 +++++++- 2 files changed, 64 insertions(+), 2 deletions(-) create mode 100644 src/main/java/ua/com/juja/study/sqlcmd/engine/JLineDemo.java diff --git a/src/main/java/ua/com/juja/study/sqlcmd/engine/JLineDemo.java b/src/main/java/ua/com/juja/study/sqlcmd/engine/JLineDemo.java new file mode 100644 index 0000000..f63b6b8 --- /dev/null +++ b/src/main/java/ua/com/juja/study/sqlcmd/engine/JLineDemo.java @@ -0,0 +1,46 @@ +package ua.com.juja.study.sqlcmd.engine; + +import jline.ConsoleReader; +import jline.CursorBuffer; + +import java.io.IOException; +import java.io.PrintWriter; +import java.io.Writer; + +/** + * Created with IntelliJ IDEA. + * User: viktor + * Date: 11/12/14 + * Time: 2:08 PM + */ +public class JLineDemo { + public static void main(String[] args) throws IOException { + Writer writer = new PrintWriter(System.out); + ConsoleReader consoleReader = new ConsoleReader(System.in, writer); + CursorBuffer buf = consoleReader.getCursorBuffer(); + int key = 0; + while ((key = consoleReader.readVirtualKey()) != 27) { + System.out.println(key); + switch (key) { + case 127: + consoleReader.backspace(); + break; + case 10: + consoleReader.moveCursor(0); + consoleReader.printNewline(); + buf.getBuffer().setLength(0); + buf.cursor = 0; + break; + default: + buf.write((char) key); + writer.write(key); + } + consoleReader.flushConsole(); + } + + } + + private static String convertCodePoint(int key) { + return new StringBuilder().appendCodePoint(key).toString(); + } +} diff --git a/src/main/java/ua/com/juja/study/sqlcmd/io/SqlConsoleReader.java b/src/main/java/ua/com/juja/study/sqlcmd/io/SqlConsoleReader.java index 0700d73..996e353 100644 --- a/src/main/java/ua/com/juja/study/sqlcmd/io/SqlConsoleReader.java +++ b/src/main/java/ua/com/juja/study/sqlcmd/io/SqlConsoleReader.java @@ -16,6 +16,8 @@ public class SqlConsoleReader { public static final int SEMICOLON = 59; public static final int NEW_LINE = 10; public static final int BACKSPACE = 127; + public static final int LEFT = 2; + public static final int RIGHT = 6; private ConsoleReader consoleReader; private Writer writer; private CursorBuffer cursorBuffer; @@ -45,12 +47,18 @@ public String readQuery() throws IOException { case BACKSPACE: consoleReader.backspace(); break; - case 2: + case LEFT: consoleReader.moveCursor(-1); break; - case 6: + case RIGHT: consoleReader.moveCursor(1); break; + case 5: + moveToEnd(); + break; + case 1: + moveToBegin(); + break; default: cursorBuffer.write((char) key); writer.write(key); @@ -61,6 +69,14 @@ public String readQuery() throws IOException { return sb.toString(); } + private void moveToBegin() throws IOException { + consoleReader.moveCursor(-cursorBuffer.length()); + } + + private void moveToEnd() throws IOException { + consoleReader.moveCursor(cursorBuffer.length() - cursorBuffer.cursor); + } + private void newLine() throws IOException { consoleReader.moveCursor(0); consoleReader.printNewline(); From 71fb23b6e991633b9ccdd23f4740f2364041d11e Mon Sep 17 00:00:00 2001 From: Viktor Kuchyn Date: Tue, 18 Nov 2014 13:21:03 +0800 Subject: [PATCH 19/39] extracted codes to constants --- .../ua/com/juja/study/sqlcmd/io/SqlConsoleReader.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/main/java/ua/com/juja/study/sqlcmd/io/SqlConsoleReader.java b/src/main/java/ua/com/juja/study/sqlcmd/io/SqlConsoleReader.java index 996e353..394fa73 100644 --- a/src/main/java/ua/com/juja/study/sqlcmd/io/SqlConsoleReader.java +++ b/src/main/java/ua/com/juja/study/sqlcmd/io/SqlConsoleReader.java @@ -18,6 +18,8 @@ public class SqlConsoleReader { public static final int BACKSPACE = 127; public static final int LEFT = 2; public static final int RIGHT = 6; + public static final int END = 5; + public static final int HOME = 1; private ConsoleReader consoleReader; private Writer writer; private CursorBuffer cursorBuffer; @@ -53,12 +55,14 @@ public String readQuery() throws IOException { case RIGHT: consoleReader.moveCursor(1); break; - case 5: + case END: moveToEnd(); break; - case 1: + case HOME: moveToBegin(); break; + case 98: + default: cursorBuffer.write((char) key); writer.write(key); From 53c896c3a0a2db19f27fae4a6f08aaeb02ac13b5 Mon Sep 17 00:00:00 2001 From: Viktor Kuchyn Date: Tue, 18 Nov 2014 13:21:58 +0800 Subject: [PATCH 20/39] Removed demo class from project --- .../juja/study/sqlcmd/engine/JLineDemo.java | 46 ------------------- 1 file changed, 46 deletions(-) delete mode 100644 src/main/java/ua/com/juja/study/sqlcmd/engine/JLineDemo.java diff --git a/src/main/java/ua/com/juja/study/sqlcmd/engine/JLineDemo.java b/src/main/java/ua/com/juja/study/sqlcmd/engine/JLineDemo.java deleted file mode 100644 index f63b6b8..0000000 --- a/src/main/java/ua/com/juja/study/sqlcmd/engine/JLineDemo.java +++ /dev/null @@ -1,46 +0,0 @@ -package ua.com.juja.study.sqlcmd.engine; - -import jline.ConsoleReader; -import jline.CursorBuffer; - -import java.io.IOException; -import java.io.PrintWriter; -import java.io.Writer; - -/** - * Created with IntelliJ IDEA. - * User: viktor - * Date: 11/12/14 - * Time: 2:08 PM - */ -public class JLineDemo { - public static void main(String[] args) throws IOException { - Writer writer = new PrintWriter(System.out); - ConsoleReader consoleReader = new ConsoleReader(System.in, writer); - CursorBuffer buf = consoleReader.getCursorBuffer(); - int key = 0; - while ((key = consoleReader.readVirtualKey()) != 27) { - System.out.println(key); - switch (key) { - case 127: - consoleReader.backspace(); - break; - case 10: - consoleReader.moveCursor(0); - consoleReader.printNewline(); - buf.getBuffer().setLength(0); - buf.cursor = 0; - break; - default: - buf.write((char) key); - writer.write(key); - } - consoleReader.flushConsole(); - } - - } - - private static String convertCodePoint(int key) { - return new StringBuilder().appendCodePoint(key).toString(); - } -} From 1039f4a4621a78d86d157a49cef1a51c0af524db Mon Sep 17 00:00:00 2001 From: Viktor Kuchyn Date: Tue, 18 Nov 2014 13:28:12 +0800 Subject: [PATCH 21/39] Added JLineDemo to gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 9724de5..ef701bd 100644 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,4 @@ pom.xml *.iws # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml hs_err_pid* +JLineDemo.java From c48b53eac5bcbd7302ce405c8a9a62d6fe28c228 Mon Sep 17 00:00:00 2001 From: Viktor Kuchyn Date: Wed, 19 Nov 2014 13:28:14 +0800 Subject: [PATCH 22/39] Extracted code constants to class --- .../java/ua/com/juja/study/sqlcmd/io/KeyCodes.java | 13 +++++++++++++ .../com/juja/study/sqlcmd/io/SqlConsoleReader.java | 13 ++++--------- 2 files changed, 17 insertions(+), 9 deletions(-) create mode 100644 src/main/java/ua/com/juja/study/sqlcmd/io/KeyCodes.java diff --git a/src/main/java/ua/com/juja/study/sqlcmd/io/KeyCodes.java b/src/main/java/ua/com/juja/study/sqlcmd/io/KeyCodes.java new file mode 100644 index 0000000..fb2c5c5 --- /dev/null +++ b/src/main/java/ua/com/juja/study/sqlcmd/io/KeyCodes.java @@ -0,0 +1,13 @@ +package ua.com.juja.study.sqlcmd.io; + +public class KeyCodes { + public static final int SEMICOLON = 59; + public static final int NEW_LINE = 10; + public static final int BACKSPACE = 127; + public static final int LEFT = 2; + public static final int RIGHT = 6; + public static final int END = 5; + public static final int HOME = 1; + public static final int UP = 16; + public static final int DOWN = 14; +} \ No newline at end of file diff --git a/src/main/java/ua/com/juja/study/sqlcmd/io/SqlConsoleReader.java b/src/main/java/ua/com/juja/study/sqlcmd/io/SqlConsoleReader.java index 394fa73..903001f 100644 --- a/src/main/java/ua/com/juja/study/sqlcmd/io/SqlConsoleReader.java +++ b/src/main/java/ua/com/juja/study/sqlcmd/io/SqlConsoleReader.java @@ -5,6 +5,8 @@ import java.io.*; +import static ua.com.juja.study.sqlcmd.io.KeyCodes.*; + /** * Created with IntelliJ IDEA. * User: viktor @@ -13,13 +15,6 @@ */ public class SqlConsoleReader { - public static final int SEMICOLON = 59; - public static final int NEW_LINE = 10; - public static final int BACKSPACE = 127; - public static final int LEFT = 2; - public static final int RIGHT = 6; - public static final int END = 5; - public static final int HOME = 1; private ConsoleReader consoleReader; private Writer writer; private CursorBuffer cursorBuffer; @@ -37,7 +32,7 @@ public String readQuery() throws IOException { while (!exit) { int key = consoleReader.readVirtualKey(); switch (key) { - case NEW_LINE: + case KeyCodes.NEW_LINE: sb.append(cursorBuffer.toString()); if (previousKey == SEMICOLON) { exit = true; @@ -58,7 +53,7 @@ public String readQuery() throws IOException { case END: moveToEnd(); break; - case HOME: + case KeyCodes.HOME: moveToBegin(); break; case 98: From e81a19bef993742960106c7675f0d4e0d5649fdc Mon Sep 17 00:00:00 2001 From: Viktor Kuchyn Date: Fri, 21 Nov 2014 12:11:04 +0800 Subject: [PATCH 23/39] Added DI interface --- .../study/sqlcmd/di/ApplicationContext.java | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 src/main/java/ua/com/juja/study/sqlcmd/di/ApplicationContext.java diff --git a/src/main/java/ua/com/juja/study/sqlcmd/di/ApplicationContext.java b/src/main/java/ua/com/juja/study/sqlcmd/di/ApplicationContext.java new file mode 100644 index 0000000..ca66c65 --- /dev/null +++ b/src/main/java/ua/com/juja/study/sqlcmd/di/ApplicationContext.java @@ -0,0 +1,20 @@ +package ua.com.juja.study.sqlcmd.di; + +import ua.com.juja.study.sqlcmd.database.DatabaseExecutor; +import ua.com.juja.study.sqlcmd.engine.KeyboardManager; +import ua.com.juja.study.sqlcmd.sql.QueryHistory; + +/** + * Created with IntelliJ IDEA. + * User: viktor + * Date: 11/19/14 + * Time: 2:14 PM + */ +public interface ApplicationContext { + + public QueryHistory getQueryHistory(); + + public DatabaseExecutor getDatabaseExecutor(); + + public KeyboardManager getKeyboardManager(); +} From debdc8789b49e983dc183c63b7d9f300a43ebe87 Mon Sep 17 00:00:00 2001 From: Viktor Kuchyn Date: Wed, 19 Nov 2014 14:07:46 +0800 Subject: [PATCH 24/39] processed exceptions --- .../java/ua/com/juja/study/sqlcmd/SqlCmd.java | 42 +++++-------------- .../sqlcmd/config/SqlCmdConfigValidator.java | 39 +++++++++++++++++ .../sqlcmd/config/ValidationException.java | 17 ++++++++ .../sqlcmd/database/DatabaseException.java | 28 +++++++++++++ .../sqlcmd/database/DatabaseExecutor.java | 8 ++-- .../study/sqlcmd/engine/KeyboardManager.java | 7 +++- 6 files changed, 104 insertions(+), 37 deletions(-) create mode 100644 src/main/java/ua/com/juja/study/sqlcmd/config/SqlCmdConfigValidator.java create mode 100644 src/main/java/ua/com/juja/study/sqlcmd/config/ValidationException.java create mode 100644 src/main/java/ua/com/juja/study/sqlcmd/database/DatabaseException.java diff --git a/src/main/java/ua/com/juja/study/sqlcmd/SqlCmd.java b/src/main/java/ua/com/juja/study/sqlcmd/SqlCmd.java index 543d0c5..fb248c9 100644 --- a/src/main/java/ua/com/juja/study/sqlcmd/SqlCmd.java +++ b/src/main/java/ua/com/juja/study/sqlcmd/SqlCmd.java @@ -1,15 +1,22 @@ package ua.com.juja.study.sqlcmd; import ua.com.juja.study.sqlcmd.config.SqlCmdConfig; +import ua.com.juja.study.sqlcmd.config.ValidationException; + +import static ua.com.juja.study.sqlcmd.config.SqlCmdConfigValidator.validateCmdOption; /** */ public class SqlCmd { public static void main(String[] args) { SqlCmdConfig config = parseCmdOption(args); - if (!validateCmdOption(config)) - System.out.println("args[] is correct "); - else System.exit(1); + try { + validateCmdOption(config); + } catch (ValidationException e) { + System.err.println("Invalid initial parameters. Unable to continue working"); + System.err.println(e.getMessage()); + System.exit(1); + } } public static SqlCmdConfig parseCmdOption(String[] args) { @@ -50,33 +57,4 @@ public static SqlCmdConfig parseCmdOption(String[] args) { return config; } - public static boolean validateCmdOption(SqlCmdConfig config) { - StringBuilder strError = new StringBuilder(); - boolean error = false; - - if ((config.getUserName() == null) || config.getUserName().isEmpty()) { - error = true; - strError.append("value arg user name is incorrect;"); - } - - if (config.getPassword() == null || config.getPassword().isEmpty()) { - error = true; - strError.append("value arg user password is incorrect;"); - } - - if (config.getDbUrl() == null || config.getDbUrl().isEmpty()) { - error = true; - strError.append("value arg db url is incorrect;"); - } - - if (config.getDriverName() == null || config.getDriverName().isEmpty()) { - error = true; - strError.append("value arg driver name is incorrect;"); - } - - if (error) - System.out.println(strError.toString()); - - return error; - } } diff --git a/src/main/java/ua/com/juja/study/sqlcmd/config/SqlCmdConfigValidator.java b/src/main/java/ua/com/juja/study/sqlcmd/config/SqlCmdConfigValidator.java new file mode 100644 index 0000000..d667349 --- /dev/null +++ b/src/main/java/ua/com/juja/study/sqlcmd/config/SqlCmdConfigValidator.java @@ -0,0 +1,39 @@ +package ua.com.juja.study.sqlcmd.config; + +/** + * Created with IntelliJ IDEA. + * User: viktor + * Date: 11/19/14 + * Time: 1:41 PM + */ +public class SqlCmdConfigValidator { + public static void validateCmdOption(SqlCmdConfig config) throws ValidationException { + StringBuilder strError = new StringBuilder(); + boolean error = false; + + if ((config.getUserName() == null) || config.getUserName().isEmpty()) { + error = true; + strError.append("value arg user name is incorrect;"); + } + + if (config.getPassword() == null || config.getPassword().isEmpty()) { + error = true; + strError.append("value arg user password is incorrect;"); + } + + if (config.getDbUrl() == null || config.getDbUrl().isEmpty()) { + error = true; + strError.append("value arg db url is incorrect;"); + } + + if (config.getDriverName() == null || config.getDriverName().isEmpty()) { + error = true; + strError.append("value arg driver name is incorrect;"); + } + + if (error) { + throw new ValidationException(strError.toString()); + } + } + +} diff --git a/src/main/java/ua/com/juja/study/sqlcmd/config/ValidationException.java b/src/main/java/ua/com/juja/study/sqlcmd/config/ValidationException.java new file mode 100644 index 0000000..0169a61 --- /dev/null +++ b/src/main/java/ua/com/juja/study/sqlcmd/config/ValidationException.java @@ -0,0 +1,17 @@ +package ua.com.juja.study.sqlcmd.config; + +/** + * Created with IntelliJ IDEA. + * User: viktor + * Date: 11/19/14 + * Time: 1:44 PM + */ +public class ValidationException extends Exception { + + public ValidationException() { + } + + public ValidationException(String message) { + super(message); + } +} diff --git a/src/main/java/ua/com/juja/study/sqlcmd/database/DatabaseException.java b/src/main/java/ua/com/juja/study/sqlcmd/database/DatabaseException.java new file mode 100644 index 0000000..2da7089 --- /dev/null +++ b/src/main/java/ua/com/juja/study/sqlcmd/database/DatabaseException.java @@ -0,0 +1,28 @@ +package ua.com.juja.study.sqlcmd.database; + +/** + * Created with IntelliJ IDEA. + * User: viktor + * Date: 11/19/14 + * Time: 1:50 PM + */ +public class DatabaseException extends Exception { + + private String query; + + public DatabaseException() { + } + + public DatabaseException(String message) { + super(message); + } + + public DatabaseException(String message, String query) { + super(message); + this.query = query; + } + + public String getQuery() { + return query; + } +} diff --git a/src/main/java/ua/com/juja/study/sqlcmd/database/DatabaseExecutor.java b/src/main/java/ua/com/juja/study/sqlcmd/database/DatabaseExecutor.java index 50e9822..9253227 100644 --- a/src/main/java/ua/com/juja/study/sqlcmd/database/DatabaseExecutor.java +++ b/src/main/java/ua/com/juja/study/sqlcmd/database/DatabaseExecutor.java @@ -10,11 +10,11 @@ */ public interface DatabaseExecutor { - public abstract boolean connectToDb(SqlCmdConfig config); + public abstract boolean connectToDb(SqlCmdConfig config) throws DatabaseException; - public abstract Row[] executeSqlScript(String sqlScript); + public abstract Row[] executeSqlScript(String sqlScript) throws DatabaseException; - public abstract String[] getDatabaseList(); + public abstract String[] getDatabaseList() throws DatabaseException; - public abstract void changeDatabase(String databaseName); + public abstract void changeDatabase(String databaseName) throws DatabaseException; } diff --git a/src/main/java/ua/com/juja/study/sqlcmd/engine/KeyboardManager.java b/src/main/java/ua/com/juja/study/sqlcmd/engine/KeyboardManager.java index e53ed30..0475ddd 100644 --- a/src/main/java/ua/com/juja/study/sqlcmd/engine/KeyboardManager.java +++ b/src/main/java/ua/com/juja/study/sqlcmd/engine/KeyboardManager.java @@ -47,6 +47,11 @@ public void startListenUserKeyboard() throws IOException { public Row[] executeQuery(String query) { history.addQueryToTheHead(query); - return databaseExecutor.executeSqlScript(query); + try { + return databaseExecutor.executeSqlScript(query); + } catch (Exception e) { + System.out.println("Got exception when execute script " + e.getMessage()); + return new Row[]{}; + } } } From 610e95f97cd0ce77a632d81deb01cb9cb8e8f66d Mon Sep 17 00:00:00 2001 From: Viktor Kuchyn Date: Fri, 21 Nov 2014 11:40:23 +0800 Subject: [PATCH 25/39] Dependency injection engine --- .../java/ua/com/juja/study/sqlcmd/SqlCmd.java | 17 +++++++ .../sqlcmd/di/DefaultApplicationContext.java | 44 +++++++++++++++++++ 2 files changed, 61 insertions(+) create mode 100644 src/main/java/ua/com/juja/study/sqlcmd/di/DefaultApplicationContext.java diff --git a/src/main/java/ua/com/juja/study/sqlcmd/SqlCmd.java b/src/main/java/ua/com/juja/study/sqlcmd/SqlCmd.java index fb248c9..ae553c8 100644 --- a/src/main/java/ua/com/juja/study/sqlcmd/SqlCmd.java +++ b/src/main/java/ua/com/juja/study/sqlcmd/SqlCmd.java @@ -2,20 +2,37 @@ import ua.com.juja.study.sqlcmd.config.SqlCmdConfig; import ua.com.juja.study.sqlcmd.config.ValidationException; +import ua.com.juja.study.sqlcmd.di.ApplicationContext; +import ua.com.juja.study.sqlcmd.di.DefaultApplicationContext; + +import java.io.IOException; import static ua.com.juja.study.sqlcmd.config.SqlCmdConfigValidator.validateCmdOption; /** */ public class SqlCmd { + + private static ApplicationContext applicationContext; + + public static ApplicationContext getApplicationContext() { + return applicationContext; + } + public static void main(String[] args) { SqlCmdConfig config = parseCmdOption(args); try { validateCmdOption(config); + applicationContext = new DefaultApplicationContext(config); + applicationContext.getKeyboardManager().startListenUserKeyboard(); } catch (ValidationException e) { System.err.println("Invalid initial parameters. Unable to continue working"); System.err.println(e.getMessage()); System.exit(1); + } catch (IOException e) { + System.err.println("Error with input/output happened. Unable to continue working"); + System.err.println(e.getMessage()); + System.exit(1); } } diff --git a/src/main/java/ua/com/juja/study/sqlcmd/di/DefaultApplicationContext.java b/src/main/java/ua/com/juja/study/sqlcmd/di/DefaultApplicationContext.java new file mode 100644 index 0000000..82644b9 --- /dev/null +++ b/src/main/java/ua/com/juja/study/sqlcmd/di/DefaultApplicationContext.java @@ -0,0 +1,44 @@ +package ua.com.juja.study.sqlcmd.di; + +import ua.com.juja.study.sqlcmd.config.SqlCmdConfig; +import ua.com.juja.study.sqlcmd.database.DatabaseExecutor; +import ua.com.juja.study.sqlcmd.database.mock.MockDatabaseExecutor; +import ua.com.juja.study.sqlcmd.engine.KeyboardManager; +import ua.com.juja.study.sqlcmd.sql.ArrayQueryHistory; +import ua.com.juja.study.sqlcmd.sql.QueryHistory; + +/** + * Created with IntelliJ IDEA. + * User: viktor + * Date: 11/19/14 + * Time: 2:20 PM + */ +public class DefaultApplicationContext implements ApplicationContext { + + private SqlCmdConfig config; + private QueryHistory queryHistory; + private DatabaseExecutor databaseExecutor; + private KeyboardManager keyboardManager; + + public DefaultApplicationContext(SqlCmdConfig config) { + this.config = config; + databaseExecutor = new MockDatabaseExecutor(); + queryHistory = new ArrayQueryHistory(); + keyboardManager = new KeyboardManager(queryHistory, databaseExecutor); + } + + @Override + public QueryHistory getQueryHistory() { + return queryHistory; + } + + @Override + public DatabaseExecutor getDatabaseExecutor() { + return databaseExecutor; + } + + @Override + public KeyboardManager getKeyboardManager() { + return keyboardManager; + } +} From 67988eb8475f2ef5e2e20461678a23fc5a8c701f Mon Sep 17 00:00:00 2001 From: Viktor Kuchyn Date: Fri, 28 Nov 2014 22:00:37 +0700 Subject: [PATCH 26/39] Templates for homework 7 - parallel executing --- .../java/ua/com/juja/study/sqlcmd/SqlCmd.java | 2 + .../sqlcmd/database/DatabaseExecutor.java | 2 +- .../study/sqlcmd/database/QueryResult.java | 50 ++++++++++++++ .../database/mock/MockDatabaseExecutor.java | 5 +- .../study/sqlcmd/di/ApplicationContext.java | 6 ++ .../study/sqlcmd/engine/KeyboardManager.java | 6 +- .../study/sqlcmd/engine/QueryFormatter.java | 21 ++++++ .../io/ConsoleFormattedQueryResultWriter.java | 67 +++++++++++++++++++ .../juja/study/sqlcmd/io/ResultWriter.java | 15 +++++ 9 files changed, 169 insertions(+), 5 deletions(-) create mode 100644 src/main/java/ua/com/juja/study/sqlcmd/database/QueryResult.java create mode 100644 src/main/java/ua/com/juja/study/sqlcmd/engine/QueryFormatter.java create mode 100644 src/main/java/ua/com/juja/study/sqlcmd/io/ConsoleFormattedQueryResultWriter.java create mode 100644 src/main/java/ua/com/juja/study/sqlcmd/io/ResultWriter.java diff --git a/src/main/java/ua/com/juja/study/sqlcmd/SqlCmd.java b/src/main/java/ua/com/juja/study/sqlcmd/SqlCmd.java index ae553c8..b03ef6b 100644 --- a/src/main/java/ua/com/juja/study/sqlcmd/SqlCmd.java +++ b/src/main/java/ua/com/juja/study/sqlcmd/SqlCmd.java @@ -33,6 +33,8 @@ public static void main(String[] args) { System.err.println("Error with input/output happened. Unable to continue working"); System.err.println(e.getMessage()); System.exit(1); + } finally { + applicationContext.shutdown(); } } diff --git a/src/main/java/ua/com/juja/study/sqlcmd/database/DatabaseExecutor.java b/src/main/java/ua/com/juja/study/sqlcmd/database/DatabaseExecutor.java index 9253227..52b3826 100644 --- a/src/main/java/ua/com/juja/study/sqlcmd/database/DatabaseExecutor.java +++ b/src/main/java/ua/com/juja/study/sqlcmd/database/DatabaseExecutor.java @@ -12,7 +12,7 @@ public interface DatabaseExecutor { public abstract boolean connectToDb(SqlCmdConfig config) throws DatabaseException; - public abstract Row[] executeSqlScript(String sqlScript) throws DatabaseException; + public abstract QueryResult executeSqlScript(String sqlScript) throws DatabaseException; public abstract String[] getDatabaseList() throws DatabaseException; diff --git a/src/main/java/ua/com/juja/study/sqlcmd/database/QueryResult.java b/src/main/java/ua/com/juja/study/sqlcmd/database/QueryResult.java new file mode 100644 index 0000000..7606bff --- /dev/null +++ b/src/main/java/ua/com/juja/study/sqlcmd/database/QueryResult.java @@ -0,0 +1,50 @@ +package ua.com.juja.study.sqlcmd.database; + +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; + +/** + * Created with IntelliJ IDEA. + * User: viktor + * Date: 11/27/14 + * Time: 12:30 PM + */ +public class QueryResult { + private Row[] rowList; + private Future futureRowList; + private String[] columnNames; + + public QueryResult(Row[] rowList) { + this.rowList = rowList; + } + + public QueryResult(Future
futureRowList) { + this.futureRowList = futureRowList; + } + + public Row[] getRowList() throws DatabaseException { + if (futureRowList == null) { + return rowList; + } + try { + return futureRowList.get(); + } catch (InterruptedException | ExecutionException e) { + throw new DatabaseException("Exception with async execution " + e.getMessage()); + } + } + + public boolean isReady() { + if (futureRowList == null) { + return true; + } + return futureRowList.isDone(); + } + + public String[] getColumnNames() { + return columnNames; + } + + public void setColumnNames(String[] columnNames) { + this.columnNames = columnNames; + } +} diff --git a/src/main/java/ua/com/juja/study/sqlcmd/database/mock/MockDatabaseExecutor.java b/src/main/java/ua/com/juja/study/sqlcmd/database/mock/MockDatabaseExecutor.java index 01c9f7e..c90bef0 100644 --- a/src/main/java/ua/com/juja/study/sqlcmd/database/mock/MockDatabaseExecutor.java +++ b/src/main/java/ua/com/juja/study/sqlcmd/database/mock/MockDatabaseExecutor.java @@ -2,6 +2,7 @@ import ua.com.juja.study.sqlcmd.config.SqlCmdConfig; import ua.com.juja.study.sqlcmd.database.DatabaseExecutor; +import ua.com.juja.study.sqlcmd.database.QueryResult; import ua.com.juja.study.sqlcmd.database.Row; /** @@ -19,8 +20,8 @@ public boolean connectToDb(SqlCmdConfig config) { } @Override - public Row[] executeSqlScript(String sqlScript) { - return new Row[]{}; + public QueryResult executeSqlScript(String sqlScript) { + return new QueryResult(new Row[]{}); } @Override diff --git a/src/main/java/ua/com/juja/study/sqlcmd/di/ApplicationContext.java b/src/main/java/ua/com/juja/study/sqlcmd/di/ApplicationContext.java index ca66c65..138d873 100644 --- a/src/main/java/ua/com/juja/study/sqlcmd/di/ApplicationContext.java +++ b/src/main/java/ua/com/juja/study/sqlcmd/di/ApplicationContext.java @@ -4,6 +4,8 @@ import ua.com.juja.study.sqlcmd.engine.KeyboardManager; import ua.com.juja.study.sqlcmd.sql.QueryHistory; +import java.util.concurrent.ExecutorService; + /** * Created with IntelliJ IDEA. * User: viktor @@ -17,4 +19,8 @@ public interface ApplicationContext { public DatabaseExecutor getDatabaseExecutor(); public KeyboardManager getKeyboardManager(); + + public ExecutorService getExecutorService(); + + public void shutdown(); } diff --git a/src/main/java/ua/com/juja/study/sqlcmd/engine/KeyboardManager.java b/src/main/java/ua/com/juja/study/sqlcmd/engine/KeyboardManager.java index 0475ddd..371470e 100644 --- a/src/main/java/ua/com/juja/study/sqlcmd/engine/KeyboardManager.java +++ b/src/main/java/ua/com/juja/study/sqlcmd/engine/KeyboardManager.java @@ -1,6 +1,7 @@ package ua.com.juja.study.sqlcmd.engine; import ua.com.juja.study.sqlcmd.database.DatabaseExecutor; +import ua.com.juja.study.sqlcmd.database.QueryResult; import ua.com.juja.study.sqlcmd.database.Row; import ua.com.juja.study.sqlcmd.sql.QueryHistory; @@ -45,13 +46,14 @@ public void startListenUserKeyboard() throws IOException { } } - public Row[] executeQuery(String query) { + public QueryResult executeQuery(String query) { history.addQueryToTheHead(query); try { return databaseExecutor.executeSqlScript(query); } catch (Exception e) { + e.printStackTrace(); System.out.println("Got exception when execute script " + e.getMessage()); - return new Row[]{}; + return new QueryResult(new Row[]{}); } } } diff --git a/src/main/java/ua/com/juja/study/sqlcmd/engine/QueryFormatter.java b/src/main/java/ua/com/juja/study/sqlcmd/engine/QueryFormatter.java new file mode 100644 index 0000000..4ad9ac6 --- /dev/null +++ b/src/main/java/ua/com/juja/study/sqlcmd/engine/QueryFormatter.java @@ -0,0 +1,21 @@ +package ua.com.juja.study.sqlcmd.engine; + +import ua.com.juja.study.sqlcmd.database.DatabaseException; +import ua.com.juja.study.sqlcmd.database.QueryResult; + +/** + * Created with IntelliJ IDEA. + * User: viktor + * Date: 11/27/14 + * Time: 1:06 PM + */ +public class QueryFormatter { + public String formatQueryResult(QueryResult queryResult) { + try { + queryResult.getRowList(); + } catch (DatabaseException e) { + return e.getMessage(); + } + return queryResult.toString(); + } +} diff --git a/src/main/java/ua/com/juja/study/sqlcmd/io/ConsoleFormattedQueryResultWriter.java b/src/main/java/ua/com/juja/study/sqlcmd/io/ConsoleFormattedQueryResultWriter.java new file mode 100644 index 0000000..8556ee9 --- /dev/null +++ b/src/main/java/ua/com/juja/study/sqlcmd/io/ConsoleFormattedQueryResultWriter.java @@ -0,0 +1,67 @@ +package ua.com.juja.study.sqlcmd.io; + +import ua.com.juja.study.sqlcmd.database.QueryResult; +import ua.com.juja.study.sqlcmd.di.ApplicationContext; +import ua.com.juja.study.sqlcmd.engine.QueryFormatter; + +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.io.Writer; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutorService; + +/** + * Created with IntelliJ IDEA. + * User: viktor + * Date: 11/27/14 + * Time: 1:00 PM + */ +public class ConsoleFormattedQueryResultWriter implements ResultWriter { + +// ___________________________________ +// | ID | NAME | BIRTHDAY | +// | 238472398234| Viktor | 25.12.1984| +// ____________________________________ + + private QueryFormatter queryFormatter = new QueryFormatter(); + private Writer writer; + private ApplicationContext applicationContext; + + public ConsoleFormattedQueryResultWriter() { + writer = new BufferedWriter(new OutputStreamWriter(System.out)); + } + + public ConsoleFormattedQueryResultWriter(Writer writer) { + this.writer = writer; + } + + @Override + public void writeQueryResult(final QueryResult queryResult) throws IOException { + if (queryResult.isReady()) { + writeQueryResultSync(queryResult); + } else { + writeQueryResultAsync(queryResult); + } + } + + private void writeQueryResultSync(QueryResult queryResult) throws IOException { + writer.write(queryFormatter.formatQueryResult(queryResult)); + writer.flush(); + } + + private void writeQueryResultAsync(final QueryResult queryResult) { + ExecutorService executorService = applicationContext.getExecutorService(); + executorService.submit(new Callable() { + @Override + public Object call() throws IOException { + writeQueryResultSync(queryResult); + return null; + } + }); + } + + public void setApplicationContext(ApplicationContext applicationContext) { + this.applicationContext = applicationContext; + } +} diff --git a/src/main/java/ua/com/juja/study/sqlcmd/io/ResultWriter.java b/src/main/java/ua/com/juja/study/sqlcmd/io/ResultWriter.java new file mode 100644 index 0000000..ca56110 --- /dev/null +++ b/src/main/java/ua/com/juja/study/sqlcmd/io/ResultWriter.java @@ -0,0 +1,15 @@ +package ua.com.juja.study.sqlcmd.io; + +import ua.com.juja.study.sqlcmd.database.QueryResult; + +import java.io.IOException; + +/** + * Created with IntelliJ IDEA. + * User: viktor + * Date: 11/27/14 + * Time: 12:57 PM + */ +public interface ResultWriter { + public void writeQueryResult(QueryResult queryResult) throws IOException; +} From 2b655bf9c385e6403847c1932c759ddeb797c887 Mon Sep 17 00:00:00 2001 From: Viktor Kuchyn
Date: Fri, 28 Nov 2014 22:02:57 +0700 Subject: [PATCH 27/39] Default methods for application context --- .../study/sqlcmd/di/DefaultApplicationContext.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/main/java/ua/com/juja/study/sqlcmd/di/DefaultApplicationContext.java b/src/main/java/ua/com/juja/study/sqlcmd/di/DefaultApplicationContext.java index 82644b9..dd0b7e8 100644 --- a/src/main/java/ua/com/juja/study/sqlcmd/di/DefaultApplicationContext.java +++ b/src/main/java/ua/com/juja/study/sqlcmd/di/DefaultApplicationContext.java @@ -7,6 +7,8 @@ import ua.com.juja.study.sqlcmd.sql.ArrayQueryHistory; import ua.com.juja.study.sqlcmd.sql.QueryHistory; +import java.util.concurrent.ExecutorService; + /** * Created with IntelliJ IDEA. * User: viktor @@ -41,4 +43,14 @@ public DatabaseExecutor getDatabaseExecutor() { public KeyboardManager getKeyboardManager() { return keyboardManager; } + + @Override + public ExecutorService getExecutorService() { + throw new UnsupportedOperationException("Not implemented"); + } + + @Override + public void shutdown() { + throw new UnsupportedOperationException("Not implemented"); + } } From 74e2aef92ab388779e7266bbfabfcbe00b46eac5 Mon Sep 17 00:00:00 2001 From: Viktor Kuchyn Date: Fri, 21 Nov 2014 11:40:23 +0800 Subject: [PATCH 28/39] Dependency injection engine --- src/main/java/ua/com/juja/study/sqlcmd/SqlCmd.java | 2 -- .../com/juja/study/sqlcmd/di/ApplicationContext.java | 6 ------ .../study/sqlcmd/di/DefaultApplicationContext.java | 12 ------------ 3 files changed, 20 deletions(-) diff --git a/src/main/java/ua/com/juja/study/sqlcmd/SqlCmd.java b/src/main/java/ua/com/juja/study/sqlcmd/SqlCmd.java index b03ef6b..ae553c8 100644 --- a/src/main/java/ua/com/juja/study/sqlcmd/SqlCmd.java +++ b/src/main/java/ua/com/juja/study/sqlcmd/SqlCmd.java @@ -33,8 +33,6 @@ public static void main(String[] args) { System.err.println("Error with input/output happened. Unable to continue working"); System.err.println(e.getMessage()); System.exit(1); - } finally { - applicationContext.shutdown(); } } diff --git a/src/main/java/ua/com/juja/study/sqlcmd/di/ApplicationContext.java b/src/main/java/ua/com/juja/study/sqlcmd/di/ApplicationContext.java index 138d873..ca66c65 100644 --- a/src/main/java/ua/com/juja/study/sqlcmd/di/ApplicationContext.java +++ b/src/main/java/ua/com/juja/study/sqlcmd/di/ApplicationContext.java @@ -4,8 +4,6 @@ import ua.com.juja.study.sqlcmd.engine.KeyboardManager; import ua.com.juja.study.sqlcmd.sql.QueryHistory; -import java.util.concurrent.ExecutorService; - /** * Created with IntelliJ IDEA. * User: viktor @@ -19,8 +17,4 @@ public interface ApplicationContext { public DatabaseExecutor getDatabaseExecutor(); public KeyboardManager getKeyboardManager(); - - public ExecutorService getExecutorService(); - - public void shutdown(); } diff --git a/src/main/java/ua/com/juja/study/sqlcmd/di/DefaultApplicationContext.java b/src/main/java/ua/com/juja/study/sqlcmd/di/DefaultApplicationContext.java index dd0b7e8..82644b9 100644 --- a/src/main/java/ua/com/juja/study/sqlcmd/di/DefaultApplicationContext.java +++ b/src/main/java/ua/com/juja/study/sqlcmd/di/DefaultApplicationContext.java @@ -7,8 +7,6 @@ import ua.com.juja.study.sqlcmd.sql.ArrayQueryHistory; import ua.com.juja.study.sqlcmd.sql.QueryHistory; -import java.util.concurrent.ExecutorService; - /** * Created with IntelliJ IDEA. * User: viktor @@ -43,14 +41,4 @@ public DatabaseExecutor getDatabaseExecutor() { public KeyboardManager getKeyboardManager() { return keyboardManager; } - - @Override - public ExecutorService getExecutorService() { - throw new UnsupportedOperationException("Not implemented"); - } - - @Override - public void shutdown() { - throw new UnsupportedOperationException("Not implemented"); - } } From 573fe9b9bd983351d2504131a22a44a5b4366156 Mon Sep 17 00:00:00 2001 From: Viktor Kuchyn Date: Thu, 27 Nov 2014 12:45:04 +0700 Subject: [PATCH 29/39] Added async execution query --- .../java/ua/com/juja/study/sqlcmd/SqlCmd.java | 2 + .../database/AsyncDatabaseExecutor.java | 52 +++++++++++++++++++ .../study/sqlcmd/database/QueryResult.java | 9 ---- .../study/sqlcmd/di/ApplicationContext.java | 6 +++ .../sqlcmd/di/DefaultApplicationContext.java | 19 ++++++- 5 files changed, 78 insertions(+), 10 deletions(-) create mode 100644 src/main/java/ua/com/juja/study/sqlcmd/database/AsyncDatabaseExecutor.java diff --git a/src/main/java/ua/com/juja/study/sqlcmd/SqlCmd.java b/src/main/java/ua/com/juja/study/sqlcmd/SqlCmd.java index ae553c8..b03ef6b 100644 --- a/src/main/java/ua/com/juja/study/sqlcmd/SqlCmd.java +++ b/src/main/java/ua/com/juja/study/sqlcmd/SqlCmd.java @@ -33,6 +33,8 @@ public static void main(String[] args) { System.err.println("Error with input/output happened. Unable to continue working"); System.err.println(e.getMessage()); System.exit(1); + } finally { + applicationContext.shutdown(); } } diff --git a/src/main/java/ua/com/juja/study/sqlcmd/database/AsyncDatabaseExecutor.java b/src/main/java/ua/com/juja/study/sqlcmd/database/AsyncDatabaseExecutor.java new file mode 100644 index 0000000..dacbc0c --- /dev/null +++ b/src/main/java/ua/com/juja/study/sqlcmd/database/AsyncDatabaseExecutor.java @@ -0,0 +1,52 @@ +package ua.com.juja.study.sqlcmd.database; + +import ua.com.juja.study.sqlcmd.SqlCmd; +import ua.com.juja.study.sqlcmd.config.SqlCmdConfig; +import ua.com.juja.study.sqlcmd.di.ApplicationContext; + +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; + +/** + * Created with IntelliJ IDEA. + * User: viktor + * Date: 11/27/14 + * Time: 12:17 PM + */ +public class AsyncDatabaseExecutor implements DatabaseExecutor { + + private DatabaseExecutor executor; + private ApplicationContext applicationContext = SqlCmd.getApplicationContext(); + + public AsyncDatabaseExecutor(DatabaseExecutor executor) { + this.executor = executor; + } + + @Override + public boolean connectToDb(SqlCmdConfig config) throws DatabaseException { + return executor.connectToDb(config); + } + + @Override + public QueryResult executeSqlScript(final String sqlScript) throws DatabaseException { + ExecutorService executorService = applicationContext.getExecutorService(); + Future queryResult = executorService.submit(new Callable
() { + @Override + public Row[] call() throws Exception { + return executor.executeSqlScript(sqlScript).getRowList(); + } + }); + return new QueryResult(queryResult); + } + + @Override + public String[] getDatabaseList() throws DatabaseException { + return executor.getDatabaseList(); + } + + @Override + public void changeDatabase(String databaseName) throws DatabaseException { + executor.changeDatabase(databaseName); + } +} diff --git a/src/main/java/ua/com/juja/study/sqlcmd/database/QueryResult.java b/src/main/java/ua/com/juja/study/sqlcmd/database/QueryResult.java index 7606bff..7e9bc7f 100644 --- a/src/main/java/ua/com/juja/study/sqlcmd/database/QueryResult.java +++ b/src/main/java/ua/com/juja/study/sqlcmd/database/QueryResult.java @@ -12,7 +12,6 @@ public class QueryResult { private Row[] rowList; private Future
futureRowList; - private String[] columnNames; public QueryResult(Row[] rowList) { this.rowList = rowList; @@ -39,12 +38,4 @@ public boolean isReady() { } return futureRowList.isDone(); } - - public String[] getColumnNames() { - return columnNames; - } - - public void setColumnNames(String[] columnNames) { - this.columnNames = columnNames; - } } diff --git a/src/main/java/ua/com/juja/study/sqlcmd/di/ApplicationContext.java b/src/main/java/ua/com/juja/study/sqlcmd/di/ApplicationContext.java index ca66c65..138d873 100644 --- a/src/main/java/ua/com/juja/study/sqlcmd/di/ApplicationContext.java +++ b/src/main/java/ua/com/juja/study/sqlcmd/di/ApplicationContext.java @@ -4,6 +4,8 @@ import ua.com.juja.study.sqlcmd.engine.KeyboardManager; import ua.com.juja.study.sqlcmd.sql.QueryHistory; +import java.util.concurrent.ExecutorService; + /** * Created with IntelliJ IDEA. * User: viktor @@ -17,4 +19,8 @@ public interface ApplicationContext { public DatabaseExecutor getDatabaseExecutor(); public KeyboardManager getKeyboardManager(); + + public ExecutorService getExecutorService(); + + public void shutdown(); } diff --git a/src/main/java/ua/com/juja/study/sqlcmd/di/DefaultApplicationContext.java b/src/main/java/ua/com/juja/study/sqlcmd/di/DefaultApplicationContext.java index 82644b9..eb41beb 100644 --- a/src/main/java/ua/com/juja/study/sqlcmd/di/DefaultApplicationContext.java +++ b/src/main/java/ua/com/juja/study/sqlcmd/di/DefaultApplicationContext.java @@ -1,12 +1,16 @@ package ua.com.juja.study.sqlcmd.di; import ua.com.juja.study.sqlcmd.config.SqlCmdConfig; +import ua.com.juja.study.sqlcmd.database.AsyncDatabaseExecutor; import ua.com.juja.study.sqlcmd.database.DatabaseExecutor; import ua.com.juja.study.sqlcmd.database.mock.MockDatabaseExecutor; import ua.com.juja.study.sqlcmd.engine.KeyboardManager; import ua.com.juja.study.sqlcmd.sql.ArrayQueryHistory; import ua.com.juja.study.sqlcmd.sql.QueryHistory; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + /** * Created with IntelliJ IDEA. * User: viktor @@ -19,12 +23,14 @@ public class DefaultApplicationContext implements ApplicationContext { private QueryHistory queryHistory; private DatabaseExecutor databaseExecutor; private KeyboardManager keyboardManager; + private ExecutorService executorService; public DefaultApplicationContext(SqlCmdConfig config) { this.config = config; - databaseExecutor = new MockDatabaseExecutor(); + databaseExecutor = new AsyncDatabaseExecutor(new MockDatabaseExecutor()); queryHistory = new ArrayQueryHistory(); keyboardManager = new KeyboardManager(queryHistory, databaseExecutor); + executorService = Executors.newFixedThreadPool(5); } @Override @@ -41,4 +47,15 @@ public DatabaseExecutor getDatabaseExecutor() { public KeyboardManager getKeyboardManager() { return keyboardManager; } + + public ExecutorService getExecutorService() { + return executorService; + } + + @Override + public void shutdown() { + executorService.shutdown(); + } + + } From 444b304c99afe9921d5ff438017596ec5f8213e3 Mon Sep 17 00:00:00 2001 From: Viktor Kuchyn
Date: Thu, 27 Nov 2014 12:54:27 +0700 Subject: [PATCH 30/39] Added async execution query: fixed problems --- .../com/juja/study/sqlcmd/database/AsyncDatabaseExecutor.java | 2 +- .../ua/com/juja/study/sqlcmd/di/DefaultApplicationContext.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/ua/com/juja/study/sqlcmd/database/AsyncDatabaseExecutor.java b/src/main/java/ua/com/juja/study/sqlcmd/database/AsyncDatabaseExecutor.java index dacbc0c..08125df 100644 --- a/src/main/java/ua/com/juja/study/sqlcmd/database/AsyncDatabaseExecutor.java +++ b/src/main/java/ua/com/juja/study/sqlcmd/database/AsyncDatabaseExecutor.java @@ -17,7 +17,6 @@ public class AsyncDatabaseExecutor implements DatabaseExecutor { private DatabaseExecutor executor; - private ApplicationContext applicationContext = SqlCmd.getApplicationContext(); public AsyncDatabaseExecutor(DatabaseExecutor executor) { this.executor = executor; @@ -30,6 +29,7 @@ public boolean connectToDb(SqlCmdConfig config) throws DatabaseException { @Override public QueryResult executeSqlScript(final String sqlScript) throws DatabaseException { + ApplicationContext applicationContext = SqlCmd.getApplicationContext(); ExecutorService executorService = applicationContext.getExecutorService(); Future queryResult = executorService.submit(new Callable
() { @Override diff --git a/src/main/java/ua/com/juja/study/sqlcmd/di/DefaultApplicationContext.java b/src/main/java/ua/com/juja/study/sqlcmd/di/DefaultApplicationContext.java index eb41beb..e3e2b1b 100644 --- a/src/main/java/ua/com/juja/study/sqlcmd/di/DefaultApplicationContext.java +++ b/src/main/java/ua/com/juja/study/sqlcmd/di/DefaultApplicationContext.java @@ -27,10 +27,10 @@ public class DefaultApplicationContext implements ApplicationContext { public DefaultApplicationContext(SqlCmdConfig config) { this.config = config; + executorService = Executors.newFixedThreadPool(5); databaseExecutor = new AsyncDatabaseExecutor(new MockDatabaseExecutor()); queryHistory = new ArrayQueryHistory(); keyboardManager = new KeyboardManager(queryHistory, databaseExecutor); - executorService = Executors.newFixedThreadPool(5); } @Override From 762af3864aa900eef3a2c18d6b18a94b3f1a71b5 Mon Sep 17 00:00:00 2001 From: Viktor Kuchyn
Date: Thu, 27 Nov 2014 13:29:28 +0700 Subject: [PATCH 31/39] Outputter --- .../ua/com/juja/study/sqlcmd/database/QueryResult.java | 9 +++++++++ .../com/juja/study/sqlcmd/engine/QueryFormatter.java | 6 ------ .../sqlcmd/io/ConsoleFormattedQueryResultWriter.java | 10 ++++------ 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/src/main/java/ua/com/juja/study/sqlcmd/database/QueryResult.java b/src/main/java/ua/com/juja/study/sqlcmd/database/QueryResult.java index 7e9bc7f..7606bff 100644 --- a/src/main/java/ua/com/juja/study/sqlcmd/database/QueryResult.java +++ b/src/main/java/ua/com/juja/study/sqlcmd/database/QueryResult.java @@ -12,6 +12,7 @@ public class QueryResult { private Row[] rowList; private Future futureRowList; + private String[] columnNames; public QueryResult(Row[] rowList) { this.rowList = rowList; @@ -38,4 +39,12 @@ public boolean isReady() { } return futureRowList.isDone(); } + + public String[] getColumnNames() { + return columnNames; + } + + public void setColumnNames(String[] columnNames) { + this.columnNames = columnNames; + } } diff --git a/src/main/java/ua/com/juja/study/sqlcmd/engine/QueryFormatter.java b/src/main/java/ua/com/juja/study/sqlcmd/engine/QueryFormatter.java index 4ad9ac6..e3d7f15 100644 --- a/src/main/java/ua/com/juja/study/sqlcmd/engine/QueryFormatter.java +++ b/src/main/java/ua/com/juja/study/sqlcmd/engine/QueryFormatter.java @@ -1,6 +1,5 @@ package ua.com.juja.study.sqlcmd.engine; -import ua.com.juja.study.sqlcmd.database.DatabaseException; import ua.com.juja.study.sqlcmd.database.QueryResult; /** @@ -11,11 +10,6 @@ */ public class QueryFormatter { public String formatQueryResult(QueryResult queryResult) { - try { - queryResult.getRowList(); - } catch (DatabaseException e) { - return e.getMessage(); - } return queryResult.toString(); } } diff --git a/src/main/java/ua/com/juja/study/sqlcmd/io/ConsoleFormattedQueryResultWriter.java b/src/main/java/ua/com/juja/study/sqlcmd/io/ConsoleFormattedQueryResultWriter.java index 8556ee9..6f22fc1 100644 --- a/src/main/java/ua/com/juja/study/sqlcmd/io/ConsoleFormattedQueryResultWriter.java +++ b/src/main/java/ua/com/juja/study/sqlcmd/io/ConsoleFormattedQueryResultWriter.java @@ -1,5 +1,6 @@ package ua.com.juja.study.sqlcmd.io; +import ua.com.juja.study.sqlcmd.SqlCmd; import ua.com.juja.study.sqlcmd.database.QueryResult; import ua.com.juja.study.sqlcmd.di.ApplicationContext; import ua.com.juja.study.sqlcmd.engine.QueryFormatter; @@ -26,7 +27,6 @@ public class ConsoleFormattedQueryResultWriter implements ResultWriter { private QueryFormatter queryFormatter = new QueryFormatter(); private Writer writer; - private ApplicationContext applicationContext; public ConsoleFormattedQueryResultWriter() { writer = new BufferedWriter(new OutputStreamWriter(System.out)); @@ -47,11 +47,11 @@ public void writeQueryResult(final QueryResult queryResult) throws IOException { private void writeQueryResultSync(QueryResult queryResult) throws IOException { writer.write(queryFormatter.formatQueryResult(queryResult)); - writer.flush(); } private void writeQueryResultAsync(final QueryResult queryResult) { - ExecutorService executorService = applicationContext.getExecutorService(); + ApplicationContext context = SqlCmd.getApplicationContext(); + ExecutorService executorService = context.getExecutorService(); executorService.submit(new Callable() { @Override public Object call() throws IOException { @@ -61,7 +61,5 @@ public Object call() throws IOException { }); } - public void setApplicationContext(ApplicationContext applicationContext) { - this.applicationContext = applicationContext; - } + } From 67e11758818f33612b4e6b0d48d263773a2a0711 Mon Sep 17 00:00:00 2001 From: Viktor Kuchyn
Date: Thu, 27 Nov 2014 13:48:06 +0700 Subject: [PATCH 32/39] Fixed output problem --- .../com/juja/study/sqlcmd/engine/QueryFormatter.java | 6 ++++++ .../sqlcmd/io/ConsoleFormattedQueryResultWriter.java | 10 ++++++---- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/main/java/ua/com/juja/study/sqlcmd/engine/QueryFormatter.java b/src/main/java/ua/com/juja/study/sqlcmd/engine/QueryFormatter.java index e3d7f15..4ad9ac6 100644 --- a/src/main/java/ua/com/juja/study/sqlcmd/engine/QueryFormatter.java +++ b/src/main/java/ua/com/juja/study/sqlcmd/engine/QueryFormatter.java @@ -1,5 +1,6 @@ package ua.com.juja.study.sqlcmd.engine; +import ua.com.juja.study.sqlcmd.database.DatabaseException; import ua.com.juja.study.sqlcmd.database.QueryResult; /** @@ -10,6 +11,11 @@ */ public class QueryFormatter { public String formatQueryResult(QueryResult queryResult) { + try { + queryResult.getRowList(); + } catch (DatabaseException e) { + return e.getMessage(); + } return queryResult.toString(); } } diff --git a/src/main/java/ua/com/juja/study/sqlcmd/io/ConsoleFormattedQueryResultWriter.java b/src/main/java/ua/com/juja/study/sqlcmd/io/ConsoleFormattedQueryResultWriter.java index 6f22fc1..8556ee9 100644 --- a/src/main/java/ua/com/juja/study/sqlcmd/io/ConsoleFormattedQueryResultWriter.java +++ b/src/main/java/ua/com/juja/study/sqlcmd/io/ConsoleFormattedQueryResultWriter.java @@ -1,6 +1,5 @@ package ua.com.juja.study.sqlcmd.io; -import ua.com.juja.study.sqlcmd.SqlCmd; import ua.com.juja.study.sqlcmd.database.QueryResult; import ua.com.juja.study.sqlcmd.di.ApplicationContext; import ua.com.juja.study.sqlcmd.engine.QueryFormatter; @@ -27,6 +26,7 @@ public class ConsoleFormattedQueryResultWriter implements ResultWriter { private QueryFormatter queryFormatter = new QueryFormatter(); private Writer writer; + private ApplicationContext applicationContext; public ConsoleFormattedQueryResultWriter() { writer = new BufferedWriter(new OutputStreamWriter(System.out)); @@ -47,11 +47,11 @@ public void writeQueryResult(final QueryResult queryResult) throws IOException { private void writeQueryResultSync(QueryResult queryResult) throws IOException { writer.write(queryFormatter.formatQueryResult(queryResult)); + writer.flush(); } private void writeQueryResultAsync(final QueryResult queryResult) { - ApplicationContext context = SqlCmd.getApplicationContext(); - ExecutorService executorService = context.getExecutorService(); + ExecutorService executorService = applicationContext.getExecutorService(); executorService.submit(new Callable() { @Override public Object call() throws IOException { @@ -61,5 +61,7 @@ public Object call() throws IOException { }); } - + public void setApplicationContext(ApplicationContext applicationContext) { + this.applicationContext = applicationContext; + } } From 60b072ced6516bade94b80b4ba74526187a4a710 Mon Sep 17 00:00:00 2001 From: Viktor Kuchyn Date: Tue, 9 Dec 2014 21:30:28 +0700 Subject: [PATCH 33/39] Prepared last task --- .gitignore | 1 + README.md | 2 +- lib/jline-1.0.jar | Bin 0 -> 91353 bytes lib/postgresql-9.3-1102.jdbc41.jar | Bin 0 -> 592322 bytes .../database/AsyncDatabaseExecutor.java | 6 --- .../sqlcmd/database/DatabaseExecutor.java | 4 -- .../study/sqlcmd/database/QueryResult.java | 2 +- .../com/juja/study/sqlcmd/database/Row.java | 15 +++++++ .../database/jdbc/JdbcDatabaseExecutor.java | 40 ++++++++++++++++++ .../database/mock/MockDatabaseExecutor.java | 7 --- 10 files changed, 58 insertions(+), 19 deletions(-) create mode 100644 lib/jline-1.0.jar create mode 100644 lib/postgresql-9.3-1102.jdbc41.jar create mode 100644 src/main/java/ua/com/juja/study/sqlcmd/database/jdbc/JdbcDatabaseExecutor.java diff --git a/.gitignore b/.gitignore index ef701bd..623e5f3 100644 --- a/.gitignore +++ b/.gitignore @@ -21,3 +21,4 @@ pom.xml # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml hs_err_pid* JLineDemo.java +!/lib/*.jar \ No newline at end of file diff --git a/README.md b/README.md index 3a044c5..b20ece8 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -sqlcmd +sqlcmd. ====== SQLCMD – Найкращий консольний клієнт баз даних написанний на Java. diff --git a/lib/jline-1.0.jar b/lib/jline-1.0.jar new file mode 100644 index 0000000000000000000000000000000000000000..42a0d90681043a435e2a16be3356e562bec12a4e GIT binary patch literal 91353 zcmZ^~1CTA>lJ?!F&C|AR+qP}nwr$(CZ5yX;+qU~Vckaypow*U;j@r8_qE=L_*!5ev zGM~(plLP{W1o+oSRxC{Tzb^j!2jcHpT3CsnT3kkiR_?!tfdk0=9cIm%JaX|106-21 z008NK50mDX5f>3wRHBg z;n3g2G~)pxt4PjEL(_(|aXVb+l(}I>;HdlTsKjzvu)Uj3?L-3e`xhE$=G($&9G1_O zYb6A XPBw#-<~ n@Y4n+J0j!q8x zhED%;N66U9+}hm9*n!5-O5f2jS$RhhQyJ;|`g<&)4o*xQPs}V*OfZh1C0K*ZKjb@1 zx?eyOxWXloN*|xdsnNaymgi|0-PFrEdye6~4+@r}kS@%{>pT$ln=@uwjoX#gN)f#9 zx1Dd6=e29kO;%&7Ztl-3QXjze$83x^?Lga9C?#agJD$EmKQk^@+9ST75%>0pbC2}< z80oMoYnydMZAtqir?R^vm$RLIxTw%8>T9FWaCF8522!Ad`ZL#x1O3HL1aU6w7( >Wq$=Y;x&g$q1Y-+y%YmunmB#?)@{?%h!Fg#=W_7Yt6N1`g36Az0@#;ee54{~m zc`ePE3zfM`9t6Xn{QM$K)=H Y^VKJTw3EK zGfo8QYBO95AouDx%(P~y!Z=LvXB_E{bA~%3&@W>0^0WJS1XWzND3Q|i26c-0@}hKh zf`vo2WNJcQxnBrpLHSp+SCT9=qj;fD1hu;BWJXV5N-Bj)$ UuXI@h!Lz*)!qN?4;1$k<_Jr$Ryb#Tv5u$}=48FiR?h z`#_tDe01iUiu4hY6bxe(v-L#;p~lFdDoRxZwR+L{AH7I{4IXqE5r7fUqbRVZA(U#W z@F)4BZ*=~V(1Aal%DNF8**bHKLPak-uPWNMHTn;SPXf~#ExmH*)g!5y!5w8rUQ)y5 zjS(>z6TVHYDm&pCDHd2&*U+H{;I`1T-c02ZMC2KVxv8Xr+;sZvAHcR9PtM(ZXthF! zp@ePSYaT&*@0V5kLFN{?^g@M^%w~&qa~BBJ^7TduXcDAn^_F0F{(QWPevhk=U0~+V z@!9VQBZ*C&An7W6s#aiBBtJwmk >*c>8JM1G&=KZ%Ng6K1WcEg|G_o0w-t-m7od0XKHVpKu=YuRi)Sm6Y9K`i^W zfgCP9!FM|3aBPgKBEl{a+`C!e?*QOQ1Wj@X!< 55 zqPfK$IO9s6ck|WT@gM{n=nmmSlX^_YfRlTy7rg_6^XBXQ`F?1+papa4nqYR~F0**} z1*>SwtO~#BWmZXb^E-2C7qywd)1V~xSuj}lU?p2}eKDmSVUb(jUJ|*PW?neUcnRww z4FP@lp}s)5`P&V_`cXSZc1gEk$_%5;OnjQ^NSYb|Wa{zW)xl5H& t$|@c&$m8Nk^J?454`+wMY7udl85v2!4gJ-~<1NqH|v&Di7E%GWmU zGHT^1qF28n5jO3wm8jM$Bq-k>d!vUxIa?7vqBXP`(`xEcZM0~K;{yU>l02Qr&xnZ4 zlL=7SD01%sv;z~;^n(`%AR2v2R{`Ucrhh=~FZ+v#Ddj`ZWb76|(5$ybJ_i*^FpYBd z95xNpZ|l#{q3t~^)WRQee0m|Wclx6w5F3jhpvo0Vi|sE9##?-7A}7n_Si2x2+;F;J zTVH~DtRjV|N8mi5XBtEaEX!ZJTeGdTNwQogL~|MukNiNao~B;X`{QJ6oav-ld}bUL zXZ)E3+b>=*1- ES?n;aX%=^bru4 zEg0tFq@;%6j%qM%izHVPeDZilIc?kefQjaY+=9b-C){Mr!Q;#qGac6-avr#5+~4mG zPEKk8sF<8j+u}uW60vL?(&pus;)SVY`xna 8xO5V<|Kp?y?+^wKN!tAT+t=v_YL5s83H8++NYT@#%U6r z_8hR!0r}6J^9eUATUftcDCG6wP1^So2Bx}<7E6?0xmvp}{l@B%jkIXmi)&DbXgnSE zMqiCT@C5wRo8)N~R+M8bi=w@jileSrv%JpjIoYDM;c3Wy^Jv6cK(a?%a_@hjN$N^% zwYta18%GXWu2|8`{L6bdHn7HPzwY{OC)|Rr%pa-_8hWqG9{~*LglQ8c?WY#3&(0XA zF^$m3=+p ^@dMg0TAUU=?YMrb0!pr0yXE?(@G$^k);fvkvBC2DG^&xVqVSfe%}JOWG<$^O z>@3kLCooTJq#E97r|2OlB-$vjJUl;z;4Wmkz1Vgs4v!^5hl3wMskYg s@c7HRGj%d6?cufOQCBS#B zRHEA#>i^|64NQlG`7f`Ue|aVP$0hjx T3dq->OOu_a*@ z$K0%L*JHOKWb8WnQ^;|Y?4mvTg~sFBl@q%jLb7^@I~2gucyCNFO+C$w;j-%hGIaQ0 zv3({}e$aeDHIl{#=5qzF3Bfp$Oq4M!+T2h{R$}CgotKha7Yx<;avB{R=MhaZB7z}w zyRnzh{Gd7RY2m*2|Jd)SrQEx{+KvPor76qLpxji~Xe7;Pm0pHtT?iyZ5Uw >p3JgqBaQx+Of845p &9l#i!;i%2vCOGE@Lu&>L%54-2Zc+h$9q%wA&o$h(# z+T%F$()O|KyW@e$2kT?KYr<7@umD- Iz%Ug)_i}Uv#_%aO zYRN%kHd~sd#dE=vr++j{e$lj~IL3LFgD-Qv2sepx&lublg5vvU{CV?;BmI+pWqgB% z^>kr5UH^n%Gms^(AB(oFCVL##NZ}b>
pu7Ccb+OiFFy49sNU2-($)} zw5p!T6eG0ZK;^ych~pkxO5~o1q*J!Iz1OsNCV>W%13A@OW__D{mNGzaEGXo49tI=o zoAKaTYJ1$ofx~AAhMlvQ*9u qC( zA}sva% 3aV=5aL>R{(A7G5GdV@UVGvtxL0&7PwK0k3 z*u=P$gDlYrNXR}T)7)K?HT*)8LV5h<_i$RP6HP19bv^wIVRE(@s)M3;zv`FMa}#$0 zc7wy)x0DV*n2(mAbqAB;#m(7_oLry)uGUnX*ohw>W@3K3h7B9mp_I z#dHm<*oepVdM6Rk6COT#m~dq=#?n;VvpK0I9694Nx=oa9#bz8qUQ1-k-ay&7sW)`U zEbk4^s4~LhN!in@QZBR4%~wh$ftw{rHW(32NU!sdOX(4zZF}S 3G$Z=Ld>e69&HdNgRdP=* zTm*~VIt$$>6*aQu^27(-49Q`{#X)=WEQ~W^L7mtzT4{q68clxnjJF `8lN3Nmc_-iNrFhLDw_rwiX12q3N)3c^9Jz9z$n+46Po@m$;i=wqy% zmV!K(9%_SitJ`p{Ug=b&aN)XN!E^VmSgD88B~6vRY`%S`5ylBDByd(3=>1`kxon1D zRuA2+pW=aP?_0pUAA431g$G67c52h6ZlN1`Gh-}F vK8ZDKS9xjK+zuOYcsWdL!$bo%@_IIIyu{|GlFVWDsD zSH~SI1J)|;I t23g!fYJhky_4C~eZV6Z6Y{9ojyjNQiK&dGP8#=Ch zw>$q@F<*t(M%3oZ3{htyJ#5w<>^vnixY}&)O&v7?vpB2p4_X&9hm!RG8^_vh53-@% zda*Nx6U;FPjCjTSy$?6OJikwOO<)34&ofe}V#qv7h<5BhFls;urQm{@o1ibU4-@yv z0fUNEnbz9Cn-m%{F<3}(_F~sswe$hLVo4@>$7b#~WFby1qe0x}0W$iG(f36IZom(p zG2Bt)RTeI0@#~^eZYV@uh-TbrK2`flFT|Q_M(Dux36RAd{>@T=T2da#eo&cdB2c|k zJxADzJYMFus*kMDF3bImyuvL3a$E5IpbeAdhh%k`G(Hqjq9h mUOG3TD{d#>TMc%SVVD~G(@wO$Qk0nb531gLoh+k;y>Q}nm zE^j!Z|MXg(H<4h&wFPJ7GmFgWNm@-l2qa3ooc08CuL-U`8m@5&w@5OMC~6Kgc|3Gh zrkd!J%|lX~=FwCQp6R}%fr3tjY$H)FM6sk+@uaxOfap04Q#A4V>dgdv4)M&1K$QUn z4}EPs6s6>nfUF#pd_qKWYCjY{fxK`KuVf0mc*9Q$BD-v+Dz9JICe?mgFD?aKxDFNJ zy@oIWBC>3LYVQ71#7JTqWK=Au{~FN<1q&_V@~S(18UGd~Pl;)Gn(iKy+W3yJ^e4U2 zeZBabozgK3Xsc1AUw4? 39~)QGfl!%+k#3i7GH?96|xU=;v0ky0)(UIpE9#t zc~UNfpi=GZYvS6;vqr1kr6c9@+#7F-7h09G-Z#wyUi?p&;F;WlU*C#NNSQ*OL`xg? zD43k9-U-k@SenEi@te1rmDBj^_Iu&ymVfcp`Wr89>1zNpYamfJqh&B`Cb6bgD&y2 vc1>p9(S(;s((B2Xgm7c1dCo>YlJZ8D-C=(~*0TJ}af=sOKRUs+l&uyBbt zM*mW&YM}uJIgphpNa9@XD#(4Z&qKtqB}%Z$K7y%$+MuN0y3~__(6+bhz}KU&PB^hA z+8&xOE}7lI$F&01&_tFc>IUu49Ka;--f3XoycHWBzzI_5zby cHGKjw{^rJ_qfw0usKXv7&G1pj%ZsoogfB zb{>=&tv&_rIqicz8tkl+#@$D0`pCqgZ03m`)qXRfK}$qR(4tbg(}PH|(PcqFF^w8p zk^RahG{v(8e>4*_ 79o lO)DCu&l^eO@!O+d}YqX=|NP&USo0hstKb}|NA0UDO(`U0?45fj~ zGN%ha*7C59l7>^@&5#WYn~$d~Dohh0UD4_3brHhyn`j7GD$}{XMs48IgfdLt?l)-E zDsa21ocQ_H!-y^1VO~9?R;&K>1?W|d_k{{OWR_*#_!dNNvWqwX{hA->I8x0fId*Gn z3A_OPGvLOwTp%aH#@RhxJ|R5@1_;8a%tH@>BAzygLYz*95^;vaV+E~i)4-6%=;gME z!Z_(nLnn;e9iwUK>wyrZPWWOYjfb2;%CgZ-dhSe<8J`_pc+kOvUF^wowu$G7HRhI02?#^S0rLqVys;QS9T%cB&5*@