Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SQLLINE-248] Extract into a separate method the logic of switching o… #257

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions src/main/java/sqlline/AbstractCommandHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,7 @@ public AbstractCommandHandler(SqlLine sqlLine, String[] names,
this.parameterCompleters =
Collections.singletonList(new NullCompleter());
} else {
List<Completer> c = new ArrayList<>(completers);
c.add(new NullCompleter());
this.parameterCompleters = c;
this.parameterCompleters = new ArrayList<>(completers);
}
}

Expand Down
32 changes: 23 additions & 9 deletions src/main/java/sqlline/Application.java
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,17 @@ public class Application {
private static final List<String> DEFAULT_CONNECTION_URL_EXAMPLES =
Collections.unmodifiableList(Arrays.asList(CONNECTION_URLS));

private static final String[] ISOLATION_LEVELS = {
"TRANSACTION_NONE",
"TRANSACTION_READ_COMMITTED",
"TRANSACTION_READ_UNCOMMITTED",
"TRANSACTION_REPEATABLE_READ",
"TRANSACTION_SERIALIZABLE"
};

private static final List<String> ISOLATION_LEVEL_LIST =
Collections.unmodifiableList(Arrays.asList(ISOLATION_LEVELS));

/** Creates an Application. */
public Application() {
}
Expand Down Expand Up @@ -211,6 +222,14 @@ public Collection<CommandHandler> getCommandHandlers(SqlLine sqlLine) {
TableNameCompleter tableCompleter = new TableNameCompleter(sqlLine);
List<Completer> empty = Collections.emptyList();
final Map<String, OutputFormat> outputFormats = getOutputFormats(sqlLine);
final Map<BuiltInProperty, Collection<String>> propertyValues =
new HashMap<BuiltInProperty, Collection<String>>() {{
put(BuiltInProperty.OUTPUT_FORMAT, outputFormats.keySet());
}};
final Map<BuiltInProperty, Collection<String>> customCompletions =
new HashMap<>();
customCompletions
.put(BuiltInProperty.OUTPUT_FORMAT, outputFormats.keySet());
final CommandHandler[] handlers = {
new ReflectiveCommandHandler(sqlLine, empty, "quit", "done", "exit"),
new ReflectiveCommandHandler(sqlLine,
Expand Down Expand Up @@ -264,9 +283,9 @@ public Collection<CommandHandler> getCommandHandlers(SqlLine sqlLine) {
new ReflectiveCommandHandler(sqlLine, empty, "rollback"),
new ReflectiveCommandHandler(sqlLine, empty, "help", "?"),
new ReflectiveCommandHandler(sqlLine,
getOpts(sqlLine).optionCompleters(), "set"),
getOpts(sqlLine).setOptionCompleters(customCompletions), "set"),
new ReflectiveCommandHandler(sqlLine,
getOpts(sqlLine).optionCompleters(), "reset"),
getOpts(sqlLine).resetOptionCompleters(), "reset"),
new ReflectiveCommandHandler(sqlLine, empty, "save"),
new ReflectiveCommandHandler(sqlLine, empty, "scan"),
new ReflectiveCommandHandler(sqlLine, empty, "sql"),
Expand Down Expand Up @@ -307,13 +326,8 @@ private Set<String> getMetadataMethodNames() {
}
}

private List<String> getIsolationLevels() {
return Arrays.asList(
"TRANSACTION_NONE",
"TRANSACTION_READ_COMMITTED",
"TRANSACTION_READ_UNCOMMITTED",
"TRANSACTION_REPEATABLE_READ",
"TRANSACTION_SERIALIZABLE");
List<String> getIsolationLevels() {
return ISOLATION_LEVEL_LIST;
}

public Map<String, HighlightStyle> getName2HighlightStyle() {
Expand Down
28 changes: 21 additions & 7 deletions src/main/java/sqlline/BuiltInProperty.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@
package sqlline;

import java.io.File;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

import org.jline.reader.LineReader;
import org.jline.reader.impl.history.DefaultHistory;
Expand All @@ -26,7 +29,8 @@ public enum BuiltInProperty implements SqlLineProperty {

AUTO_COMMIT("autoCommit", Type.BOOLEAN, true),
AUTO_SAVE("autoSave", Type.BOOLEAN, false),
COLOR_SCHEME("colorScheme", Type.STRING, DEFAULT),
COLOR_SCHEME("colorScheme", Type.STRING, DEFAULT, true, false,
new Application().getName2HighlightStyle().keySet()),
COLOR("color", Type.BOOLEAN, false),
CSV_DELIMITER("csvDelimiter", Type.STRING, ","),

Expand All @@ -41,12 +45,13 @@ public enum BuiltInProperty implements SqlLineProperty {
HISTORY_FILE("historyFile", Type.STRING,
new File(SqlLineOpts.saveDir(), "history").getAbsolutePath()),
INCREMENTAL("incremental", Type.BOOLEAN, false),
ISOLATION("isolation", Type.STRING, "TRANSACTION_REPEATABLE_READ"),
ISOLATION("isolation", Type.STRING, "TRANSACTION_REPEATABLE_READ",
true, false, new HashSet<>(new Application().getIsolationLevels())),
MAX_COLUMN_WIDTH("maxColumnWidth", Type.INTEGER, -1),
// don't save maxheight, maxwidth: it is automatically set based on
// the terminal configuration
MAX_HEIGHT("maxHeight", Type.INTEGER, 80, false, false),
MAX_WIDTH("maxWidth", Type.INTEGER, 80, false, false),
MAX_HEIGHT("maxHeight", Type.INTEGER, 80, false, false, null),
MAX_WIDTH("maxWidth", Type.INTEGER, 80, false, false, null),

MAX_HISTORY_ROWS("maxHistoryRows",
Type.INTEGER, DefaultHistory.DEFAULT_HISTORY_SIZE),
Expand All @@ -72,29 +77,34 @@ public enum BuiltInProperty implements SqlLineProperty {
TRIM_SCRIPTS("trimScripts", Type.BOOLEAN, true),
USE_LINE_CONTINUATION("useLineContinuation", Type.BOOLEAN, true),
VERBOSE("verbose", Type.BOOLEAN, false),
VERSION("version", Type.STRING, new Application().getVersion(), false, true);
VERSION("version", Type.STRING, new Application().getVersion(), false, true,
null);

private final String propertyName;
private final Type type;
private final Object defaultValue;
private final boolean couldBeStored;
private final boolean isReadOnly;
private final Set<String> availableValues;

BuiltInProperty(String propertyName, Type type, Object defaultValue) {
this(propertyName, type, defaultValue, true, false);
this(propertyName, type, defaultValue, true, false, null);
}

BuiltInProperty(
String propertyName,
Type type,
Object defaultValue,
boolean couldBeStored,
boolean isReadOnly) {
boolean isReadOnly,
Set<String> availableValues) {
this.propertyName = propertyName;
this.type = type;
this.defaultValue = defaultValue;
this.isReadOnly = isReadOnly;
this.couldBeStored = couldBeStored;
this.availableValues = availableValues == null
? Collections.emptySet() : Collections.unmodifiableSet(availableValues);
}

@Override public String propertyName() {
Expand All @@ -117,6 +127,10 @@ public enum BuiltInProperty implements SqlLineProperty {
return type;
}

@Override public Set<String> getAvailableValues() {
return availableValues;
}

/** Returns the built-in property with the given name, or null if not found.
*
* @param propertyName Property name
Expand Down
28 changes: 20 additions & 8 deletions src/main/java/sqlline/Commands.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,14 @@
import java.nio.charset.StandardCharsets;
import java.sql.*;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

import org.jline.reader.EOFError;
import org.jline.reader.History;
import org.jline.reader.Parser;
import org.jline.reader.UserInterruptException;
import org.jline.terminal.Terminal;

/**
* Collection of available commands.
Expand Down Expand Up @@ -480,6 +483,21 @@ public void dropall(String line, DispatchCallback callback) {
}
}

private int getUserAnswer(
String question, int... allowedAnswers) throws IOException {
final Set<Integer> allowedAnswerSet =
IntStream.of(allowedAnswers).boxed().collect(Collectors.toSet());
final Terminal terminal = sqlLine.getLineReader().getTerminal();
final PrintWriter writer = terminal.writer();
writer.write(question);
int c;
// The logic to prevent reaction of SqlLineParser here
do {
c = terminal.reader().read(100);
} while (c != -1 && !allowedAnswerSet.contains(c));
return c;
}

public void reconnect(String line, DispatchCallback callback) {
DatabaseConnection databaseConnection = sqlLine.getDatabaseConnection();
if (databaseConnection == null || databaseConnection.getUrl() == null) {
Expand Down Expand Up @@ -1714,14 +1732,8 @@ private void sillyLess(InputStream in) throws IOException {

// silly little pager
if (index % (sqlLine.getOpts().getMaxHeight() - 1) == 0) {
String prompt = sqlLine.loc("enter-for-more");
sqlLine.getLineReader().getTerminal().writer().write(prompt);
int c;
// The logic to prevent reaction of SqlLineParser here
do {
c = sqlLine.getLineReader().getTerminal().reader().read(100);
} while (c != -1 && c != 13 && c != 'q');
if (c == -1 || c == 'q') {
int userInput = getUserAnswer(sqlLine.loc("enter-for-more"), 'q', 13);
if (userInput == -1 || userInput == 'q') {
sqlLine.getLineReader().getTerminal().writer().write('\n');
break;
}
Expand Down
20 changes: 14 additions & 6 deletions src/main/java/sqlline/SqlLineCommandCompleter.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import java.util.LinkedList;
import java.util.List;

import org.jline.builtins.Completers;
import org.jline.reader.Completer;
import org.jline.reader.impl.completer.AggregateCompleter;
import org.jline.reader.impl.completer.ArgumentCompleter;
Expand All @@ -26,16 +27,23 @@
class SqlLineCommandCompleter extends AggregateCompleter {
SqlLineCommandCompleter(SqlLine sqlLine) {
super(new LinkedList<>());
List<ArgumentCompleter> completers = new LinkedList<>();
List<Completer> completers = new LinkedList<>();

for (CommandHandler commandHandler : sqlLine.getCommandHandlers()) {
for (String cmd : commandHandler.getNames()) {
List<Completer> compl = new LinkedList<>();
compl.add(new StringsCompleter(SqlLine.COMMAND_PREFIX + cmd));
compl.addAll(commandHandler.getParameterCompleters());
compl.add(new NullCompleter()); // last param no complete

completers.add(new ArgumentCompleter(compl));
final List<Completer> parameterCompleters =
commandHandler.getParameterCompleters();
if (parameterCompleters.size() == 1
&& parameterCompleters.iterator().next()
instanceof Completers.RegexCompleter) {
completers.add(parameterCompleters.iterator().next());
} else {
compl.add(new StringsCompleter(SqlLine.COMMAND_PREFIX + cmd));
compl.addAll(parameterCompleters);
compl.add(new NullCompleter()); // last param no complete
completers.add(new ArgumentCompleter(compl));
}
}
}

Expand Down
77 changes: 76 additions & 1 deletion src/main/java/sqlline/SqlLineOpts.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import java.util.*;
import java.util.stream.Collectors;

import org.jline.builtins.Completers;
import org.jline.keymap.KeyMap;
import org.jline.reader.Binding;
import org.jline.reader.Candidate;
Expand Down Expand Up @@ -116,10 +117,84 @@ public SqlLineOpts(SqlLine sqlLine, Properties props) {
loadProperties(props);
}

public List<Completer> optionCompleters() {
public List<Completer> resetOptionCompleters() {
return Collections.singletonList(this);
}

/**
* Builds and returns {@link org.jline.builtins.Completers.RegexCompleter} for
* <code>!set</code> command based on
* (in decreasing order of priority)
* <ul>
* <li>Customizations via {@code customCompletions}</li>
* <li>Available values defined in {@link BuiltInProperty}</li>
* <li>{@link Type} of property.
* Currently there is completion only for boolean type</li>
* </ul>
*
* @param customCompletions defines custom completions values per property
* @return a singleton list with a built RegexCompleter */
public List<Completer> setOptionCompleters(
Map<BuiltInProperty, Collection<String>> customCompletions) {
Map<String, Completer> comp = new HashMap<>();
final String start = "START";
comp.put(start, new StringsCompleter("!set"));
Collection<BuiltInProperty> booleanProperties = new ArrayList<>();
Collection<BuiltInProperty> withDefinedAvailableValues = new ArrayList<>();
StringBuilder sb = new StringBuilder(start + " (");
for (BuiltInProperty property : BuiltInProperty.values()) {
if (customCompletions.containsKey(property)) {
continue;
} else if (!property.getAvailableValues().isEmpty()) {
withDefinedAvailableValues.add(property);
} else if (property.type() == Type.BOOLEAN) {
booleanProperties.add(property);
} else {
sb.append(property.name()).append(" | ");
comp.put(property.name(),
new StringsCompleter(property.propertyName()));
}
}
// all boolean properties without defined available values and
// not customized via {@code customCompletions} have values
// for autocompletion specified in SqlLineProperty.BOOLEAN_VALUES
final String booleanTypeString = Type.BOOLEAN.toString();
sb.append(booleanTypeString);
comp.put(booleanTypeString,
new StringsCompleter(booleanProperties
.stream()
.map(BuiltInProperty::propertyName)
.toArray(String[]::new)));
final String booleanPropertyValueKey = booleanTypeString + "_value";
comp.put(booleanPropertyValueKey,
new StringsCompleter(BuiltInProperty.BOOLEAN_VALUES));
sb.append(" ").append(booleanPropertyValueKey);
// If a property has defined values they will be used for autocompletion
for (BuiltInProperty property : withDefinedAvailableValues) {
final String propertyName = property.propertyName();
sb.append(" | ").append(propertyName);
comp.put(propertyName, new StringsCompleter(propertyName));
final String propertyValueKey = propertyName + "_value";
comp.put(propertyValueKey,
new StringsCompleter(
property.getAvailableValues().toArray(new String[0])));
sb.append(" ").append(propertyValueKey);
}
for (Map.Entry<BuiltInProperty, Collection<String>> mapEntry
: customCompletions.entrySet()) {
final String propertyName = mapEntry.getKey().propertyName();
comp.put(propertyName, new StringsCompleter(propertyName));
final String propertyValueKey = propertyName + "_value";
comp.put(propertyValueKey,
new StringsCompleter(mapEntry.getValue().toArray(new String[0])));
sb.append("| ").append(propertyName).append(" ")
.append(propertyValueKey);
}
sb.append(") ");
return Collections.singletonList(
new Completers.RegexCompleter(sb.toString(), comp::get));
}

/**
* The save directory if HOME/.sqlline/ on UNIX, and HOME/sqlline/ on
* Windows.
Expand Down
11 changes: 9 additions & 2 deletions src/main/java/sqlline/SqlLineProperty.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,18 @@
*/
package sqlline;

import java.util.Set;

/**
* Definition of property that may be specified for SqlLine.
*
* @see BuiltInProperty
*/
public interface SqlLineProperty {
String DEFAULT = "default";
String[] BOOLEAN_VALUES = {
Boolean.TRUE.toString(), Boolean.FALSE.toString()};

String propertyName();

Object defaultValue();
Expand All @@ -28,6 +33,8 @@ public interface SqlLineProperty {

Type type();

Set<String> getAvailableValues();

/** Property writer. */
@FunctionalInterface
interface Writer {
Expand All @@ -38,8 +45,8 @@ interface Writer {
enum Type {
BOOLEAN,
CHAR,
STRING,
INTEGER;
INTEGER,
STRING;
}

}
Expand Down
Loading