From fc13913641138d9a5768ff2955c5e36eef2541fe Mon Sep 17 00:00:00 2001 From: lukaseder Date: Tue, 5 Feb 2019 17:35:24 +0100 Subject: [PATCH] [#8294] Add interactive mode to ParserCLI --- jOOQ/src/main/java/org/jooq/ParserCLI.java | 201 ++++++++++++++++++--- 1 file changed, 176 insertions(+), 25 deletions(-) diff --git a/jOOQ/src/main/java/org/jooq/ParserCLI.java b/jOOQ/src/main/java/org/jooq/ParserCLI.java index 14cc9499fb..1d0f32d692 100644 --- a/jOOQ/src/main/java/org/jooq/ParserCLI.java +++ b/jOOQ/src/main/java/org/jooq/ParserCLI.java @@ -37,6 +37,12 @@ */ package org.jooq; +import java.util.ArrayList; +import java.util.List; +import java.util.Scanner; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + import org.jooq.conf.RenderKeywordCase; import org.jooq.conf.RenderNameCase; import org.jooq.conf.Settings; @@ -51,24 +57,40 @@ import org.jooq.impl.ParserException; */ public final class ParserCLI { + private static final Pattern FLAG = Pattern.compile("^/([\\w\\-]+)\\s+(\\w+)\\s*$"); + public static final void main(String[] args) { Args a; + Settings settings = new Settings(); + DSLContext ctx; try { a = parse(args); + settings(a, settings); + ctx = ctx(a, settings); - if (a.toDialect == null || a.sql == null) { - System.err.println("Mandatory arguments: -t and -s. Use -h for help"); + if (a.interactive) { + interactiveMode(ctx, a); + } + else if (a.toDialect == null || a.sql == null) { + System.out.println("Mandatory arguments: -t and -s. Use -h for help"); throw new RuntimeException(); } + else { + render(ctx, a); + } } catch (Exception e) { System.exit(-1); return; } + } - Settings settings = new Settings(); + private static final DSLContext ctx(Args a, Settings settings) { + return DSL.using(a.toDialect, settings); + } + private static final void settings(Args a, Settings settings) { if (a.formatted) settings.setRenderFormatted(true); if (a.keywords != null) @@ -77,15 +99,122 @@ public final class ParserCLI { settings.setRenderNameCase(a.name); if (a.fromDialect != null) settings.setParseDialect(a.fromDialect); + } + + private static final void interactiveMode(DSLContext ctx, Args a) { + Scanner scan = new Scanner(System.in); + + System.out.print("> "); + + cliLoop: + do { + String line = scan.nextLine(); + + // TODO: Allow reading history again using arrow keys + // https://stackoverflow.com/q/572001/521799 + a.history.add(line); + + if (a.sql == null && line.startsWith("/")) { + if ("/q".equals(line) || "/quit".equals(line) || + "/e".equals(line) || "/exit".equals(line)) + break cliLoop; + else if ("/?".equals(line) || "/h".equals(line) || "/help".equals(line)) + helpInteractive(); + else if ("/d".equals(line) || "/display".equals(line)) + displayArguments(a); + else { + Matcher matcher = FLAG.matcher(line); + + if (matcher.find()) { + String flag = matcher.group(1); + String arg = matcher.group(2); + + if (flag != null && arg != null) { + if ("f".equals(flag) || "formatted".equals(flag)) { + a.formatted = Boolean.parseBoolean(arg.toLowerCase()); + } + else if ("k".equals(flag) || "keyword".equals(flag)) { + try { + a.keywords = RenderKeywordCase.valueOf(arg.toUpperCase()); + } + catch (IllegalArgumentException e) { + invalid(arg, RenderKeywordCase.class); + } + } + else if ("i".equals(flag) || "identifier".equals(flag)) { + try { + a.name = RenderNameCase.valueOf(arg.toUpperCase()); + } + catch (IllegalArgumentException e) { + invalid(arg, RenderNameCase.class); + } + } + else if ("f".equals(flag) || "from-dialect".equals(flag)) { + try { + a.fromDialect = SQLDialect.valueOf(arg.toUpperCase()); + } + catch (IllegalArgumentException e) { + invalid(arg, SQLDialect.class); + } + } + else if ("t".equals(flag) || "to-dialect".equals(flag)) { + try { + a.toDialect = SQLDialect.valueOf(arg.toUpperCase()); + } + catch (IllegalArgumentException e) { + invalid(arg, SQLDialect.class); + } + } + } + } + else { + System.out.println("Unrecognised command: " + line); + System.out.println("Type /h for help"); + } + } + + settings(a, ctx.settings()); + ctx = ctx(a, ctx.settings()); + } + + if (a.sql != null || !line.startsWith("/")) { + if (a.sql == null) + a.sql = line; + else + a.sql = a.sql + "\n" + line; + + if (a.sql.trim().endsWith(";")) { + render(ctx, a); + a.sql = null; + } + } + + System.out.print("> "); + } + while (scan.hasNextLine()); + } + + private static final void displayArguments(Args a) { + System.out.println("Formatted : " + a.formatted); + System.out.println("From dialect : " + a.fromDialect); + System.out.println("To dialect : " + a.toDialect); + System.out.println("Keywords : " + a.keywords); + System.out.println("Identifiers : " + a.name); + } + + private static final void render(DSLContext ctx, Args a) { + String sql = a.sql.trim(); - DSLContext ctx = DSL.using(a.toDialect, settings); try { - System.out.println(ctx.render(ctx.parser().parse(a.sql))); + if (sql.matches("^(?is:(?:BEGIN|DECLARE|DO|FOR|IF|LOOP|REPEAT|WHILE).*)$")) + System.out.println(ctx.render(ctx.parser().parseStatement(a.sql))); + else + System.out.println(ctx.render(ctx.parser().parse(a.sql))); } catch (ParserException e1) { ParserException e = e1; - if (!a.sql.trim().matches("^(?is:(ALTER|BEGIN|COMMENT|CREATE|DECLARE|DELETE|DESCRIBE|DROP|GRANT|INSERT|MERGE|RENAME|REVOKE|SELECT|SET|SHOW|TRUNCATE|UPDATE|USE).*)$")) { + if (!sql.matches("^(?is:(?:ALTER|BEGIN|COMMENT|CREATE|DECLARE|DELETE|DESCRIBE|DROP|GRANT|INSERT|MERGE|RENAME|REVOKE|SELECT|SET|SHOW|TRUNCATE|UPDATE|USE).*)$")) { try { System.out.println(ctx.render(ctx.parser().parseField(a.sql))); } @@ -94,7 +223,7 @@ public final class ParserCLI { } } - System.err.println(e.getMessage()); + System.out.println(e.getMessage()); } } @@ -108,7 +237,7 @@ public final class ParserCLI { } else if ("-k".equals(args[i]) || "--keyword".equals(args[i])) { try { - result.keywords = RenderKeywordCase.valueOf(args[++i]); + result.keywords = RenderKeywordCase.valueOf(args[++i].toUpperCase()); continue argsLoop; } catch (IllegalArgumentException e) { @@ -116,13 +245,13 @@ public final class ParserCLI { throw e; } catch (ArrayIndexOutOfBoundsException e) { - System.err.println("Flag -k / --keyword requires argument"); + System.out.println("Flag -k / --keyword requires argument"); throw e; } } else if ("-i".equals(args[i]) || "--identifier".equals(args[i])) { try { - result.keywords = RenderKeywordCase.valueOf(args[++i]); + result.keywords = RenderKeywordCase.valueOf(args[++i].toUpperCase()); continue argsLoop; } catch (IllegalArgumentException e) { @@ -130,11 +259,11 @@ public final class ParserCLI { throw e; } catch (ArrayIndexOutOfBoundsException e) { - System.err.println("Flag -i / --identifier requires argument"); + System.out.println("Flag -i / --identifier requires argument"); throw e; } } - else if ("-f".equals(args[i]) || "--from-dialect".equals(args[i])) { + else if ("-f".equals(args[i]) || "--from-dialect".equals(args[i].toUpperCase())) { try { result.fromDialect = SQLDialect.valueOf(args[++i]); continue argsLoop; @@ -144,13 +273,13 @@ public final class ParserCLI { throw e; } catch (ArrayIndexOutOfBoundsException e) { - System.err.println("Flag -f / --from-dialect requires argument"); + System.out.println("Flag -f / --from-dialect requires argument"); throw e; } } else if ("-t".equals(args[i]) || "--to-dialect".equals(args[i])) { try { - result.toDialect = SQLDialect.valueOf(args[++i]); + result.toDialect = SQLDialect.valueOf(args[++i].toUpperCase()); continue argsLoop; } catch (IllegalArgumentException e) { @@ -158,7 +287,7 @@ public final class ParserCLI { throw e; } catch (ArrayIndexOutOfBoundsException e) { - System.err.println("Flag -t / --to-dialect requires argument"); + System.out.println("Flag -t / --to-dialect requires argument"); throw e; } } @@ -168,16 +297,19 @@ public final class ParserCLI { continue argsLoop; } catch (ArrayIndexOutOfBoundsException e) { - System.err.println("Flag -s / --sql requires argument"); + System.out.println("Flag -s / --sql requires argument"); throw e; } } + else if ("-I".equals(args[i]) || "--interactive".equals(args[i])) { + result.interactive = true; + } else if ("-h".equals(args[i]) || "--help".equals(args[i])) { help(); throw new RuntimeException(); } else { - System.err.println("Unknown flag: " + args[i] + ". Use -h or --help"); + System.out.println("Unknown flag: " + args[i] + ". Use -h or --help"); throw new RuntimeException(); } } @@ -185,12 +317,12 @@ public final class ParserCLI { return result; } - private static void invalid(String string, Class> type) { - System.err.println("Invalid " + type.getSimpleName() + ": " + string); - System.err.println("Possible values:"); + private static final void invalid(String string, Class> type) { + System.out.println("Invalid " + type.getSimpleName() + ": " + string); + System.out.println("Possible values:"); for (Enum e : type.getEnumConstants()) - System.err.println(" " + e.name()); + System.out.println(" " + e.name()); } private static final void help() { @@ -202,14 +334,33 @@ public final class ParserCLI { System.out.println(" -f / --from-dialect Specify the input dialect (org.jooq.SQLDialect)"); System.out.println(" -t / --to-dialect Specify the output dialect (org.jooq.SQLDialect)"); System.out.println(" -s / --sql Specify the input SQL string"); + System.out.println(""); + System.out.println(" -I / --interactive Start interactive mode"); + } + + private static final void helpInteractive() { + System.out.println("Usage:"); + System.out.println(" /d or /display Display arguments"); + System.out.println(" /f or /formatted Format output SQL"); + System.out.println(" /h or /help Display this help"); + System.out.println(" /k or /keyword Specify the output keyword case (org.jooq.conf.RenderKeywordCase)"); + System.out.println(" /i or /identifier Specify the output identifier case (org.jooq.conf.RenderNameCase)"); + System.out.println(" /f or /from-dialect Specify the input dialect (org.jooq.SQLDialect)"); + System.out.println(" /t or /to-dialect Specify the output dialect (org.jooq.SQLDialect)"); + System.out.println(" Specify the input SQL string"); + System.out.println(""); + System.out.println(" /q or /quit Quit"); + System.out.println(" /e or /exit Also quit"); } public static final class Args { + List history = new ArrayList(); String sql; - RenderKeywordCase keywords; - RenderNameCase name; - SQLDialect toDialect; - SQLDialect fromDialect; + RenderKeywordCase keywords = RenderKeywordCase.LOWER; + RenderNameCase name = RenderNameCase.LOWER; + SQLDialect toDialect = SQLDialect.DEFAULT; + SQLDialect fromDialect = SQLDialect.DEFAULT; boolean formatted; + boolean interactive; } }