This repository has been archived by the owner on Apr 16, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Clean up CLI-handling by creating a new CLIService class
Closes: #21
- Loading branch information
Showing
8 changed files
with
259 additions
and
208 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
60 changes: 60 additions & 0 deletions
60
src/main/java/de/fearnixx/jeak/antlr/CommandParserUtil.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
package de.fearnixx.jeak.antlr; | ||
|
||
import de.fearnixx.jeak.service.command.CommandCtxVisitor; | ||
import de.fearnixx.jeak.service.command.CommandInfo; | ||
import de.fearnixx.jeak.service.command.SyntaxErrorListener; | ||
import de.mlessmann.confort.lang.RuntimeParseException; | ||
import org.antlr.v4.runtime.CharStreams; | ||
import org.antlr.v4.runtime.CodePointCharStream; | ||
import org.antlr.v4.runtime.CommonTokenStream; | ||
import org.antlr.v4.runtime.atn.PredictionMode; | ||
import org.slf4j.Logger; | ||
|
||
public abstract class CommandParserUtil { | ||
|
||
private CommandParserUtil() { | ||
} | ||
|
||
public static CommandInfo parseCommandLine(String arguments, Logger logger) { | ||
CodePointCharStream charStream = CharStreams.fromString(arguments); | ||
var lexer = new CommandExecutionCtxLexer(charStream); | ||
var tokenStream = new CommonTokenStream(lexer); | ||
var parser = new CommandExecutionCtxParser(tokenStream); | ||
|
||
|
||
// Use 2-stage parsing for expression performance | ||
// https://github.com/antlr/antlr4/blob/master/doc/faq/general.md#why-is-my-expression-parser-slow | ||
try { | ||
// STAGE 1 | ||
var treeVisitor = new CommandCtxVisitor(); | ||
var errorListener = new SyntaxErrorListener(treeVisitor.getInfo().getErrorMessages()::add); | ||
|
||
logger.debug("Trying to run STAGE 1 parsing. (SSL prediction)"); | ||
parser.getInterpreter().setPredictionMode(PredictionMode.SLL); | ||
parser.removeErrorListeners(); | ||
parser.addErrorListener(errorListener); | ||
var grammarContext = parser.commandExecution(); | ||
treeVisitor.visitCommandExecution(grammarContext); | ||
return treeVisitor.getInfo(); | ||
} catch (Exception ex) { | ||
// STAGE 2 | ||
var treeVisitor = new CommandCtxVisitor(); | ||
var errorListener = new SyntaxErrorListener(treeVisitor.getInfo().getErrorMessages()::add); | ||
|
||
logger.debug("Trying to run STAGE 2 parsing. (LL prediction)", ex); | ||
tokenStream.seek(0); | ||
parser.reset(); | ||
parser.getInterpreter().setPredictionMode(PredictionMode.LL); | ||
parser.removeErrorListeners(); | ||
parser.addErrorListener(errorListener); | ||
|
||
try { | ||
var grammarContext = parser.commandExecution(); | ||
treeVisitor.visitCommandExecution(grammarContext); | ||
} catch (RuntimeParseException e) { | ||
treeVisitor.getInfo().getErrorMessages().add(e.getMessage()); | ||
} | ||
return treeVisitor.getInfo(); | ||
} | ||
} | ||
} |
24 changes: 24 additions & 0 deletions
24
src/main/java/de/fearnixx/jeak/commandline/CLICommandContext.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
package de.fearnixx.jeak.commandline; | ||
|
||
import de.fearnixx.jeak.service.command.CommandInfo; | ||
|
||
import java.util.function.Consumer; | ||
|
||
public class CLICommandContext { | ||
|
||
private final CommandInfo commInfo; | ||
private final Consumer<String> messageConsumer; | ||
|
||
public CLICommandContext(CommandInfo commInfo, Consumer<String> messageConsumer) { | ||
this.commInfo = commInfo; | ||
this.messageConsumer = messageConsumer; | ||
} | ||
|
||
public CommandInfo getCommandInfo() { | ||
return commInfo; | ||
} | ||
|
||
public Consumer<String> getMessageConsumer() { | ||
return messageConsumer; | ||
} | ||
} |
81 changes: 81 additions & 0 deletions
81
src/main/java/de/fearnixx/jeak/commandline/CLIService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
package de.fearnixx.jeak.commandline; | ||
|
||
import de.fearnixx.jeak.antlr.CommandParserUtil; | ||
import de.fearnixx.jeak.service.command.CommandInfo; | ||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
|
||
import java.io.OutputStream; | ||
import java.util.Map; | ||
import java.util.Optional; | ||
import java.util.concurrent.ConcurrentHashMap; | ||
import java.util.function.Consumer; | ||
|
||
import static de.fearnixx.jeak.antlr.CommandParserUtil.parseCommandLine; | ||
|
||
public class CLIService { | ||
|
||
private static final Object INSTANCE_LOCK = new Object(); | ||
private static final Logger logger = LoggerFactory.getLogger(CLIService.getInstance().getClass()); | ||
public static CLIService instance; | ||
|
||
public synchronized static CLIService getInstance() { | ||
synchronized (INSTANCE_LOCK) { | ||
if (instance == null) { | ||
instance = new CLIService(); | ||
} | ||
return instance; | ||
} | ||
} | ||
|
||
/** | ||
* In case the framework is not started via its shipped main class, calling applications may override | ||
* the cli-command service instance so they can properly receive the command registrations. | ||
* @implNote This <em>MUST</em> be called before any interaction with the actual framework as the instance probably will be initialized by then. | ||
*/ | ||
protected static void setInstance(CLIService service) { | ||
synchronized (INSTANCE_LOCK) { | ||
if (instance != null) { | ||
throw new IllegalStateException("Replacement CLI-Services MUST be set before the first #getInstance call!"); | ||
} | ||
instance = service; | ||
} | ||
} | ||
|
||
protected final Map<String, Consumer<CLICommandContext>> cliCommands = new ConcurrentHashMap<>(); | ||
|
||
protected CLIService() { | ||
} | ||
|
||
protected Consumer<String> getMessageConsumer() { | ||
return System.out::println; | ||
} | ||
|
||
public Optional<Consumer<CLICommandContext>> registerCommand(String command, Consumer<CLICommandContext> commandConsumer) { | ||
return Optional.ofNullable(cliCommands.put(command, commandConsumer)); | ||
} | ||
|
||
public void receiveLine(String input) { | ||
int spacePos = input.indexOf(' '); | ||
String command; | ||
String contextPart = null; | ||
if (spacePos < 0) { | ||
command = input; | ||
} else { | ||
command = input.substring(0, spacePos); | ||
contextPart = input.substring(spacePos).trim(); | ||
} | ||
|
||
Consumer<CLICommandContext> cliConsumer = cliCommands.getOrDefault(command, null); | ||
if (cliConsumer != null) { | ||
CommandInfo commandInfo = parseCommandLine(contextPart, logger); | ||
CLICommandContext cliContext = new CLICommandContext(commandInfo, getMessageConsumer()); | ||
cliConsumer.accept(cliContext); | ||
|
||
} else { | ||
final String unknownMsg = String.format("Unknown command: \"%s\"", command); | ||
logger.info(unknownMsg); | ||
getMessageConsumer().accept(unknownMsg); | ||
} | ||
} | ||
} |
Oops, something went wrong.