-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
25 changed files
with
842 additions
and
45 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
Binary file not shown.
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 |
---|---|---|
@@ -1,5 +1,5 @@ | ||
distributionBase=GRADLE_USER_HOME | ||
distributionPath=wrapper/dists | ||
distributionUrl=https\://services.gradle.org/distributions/gradle-4.7-bin.zip | ||
distributionUrl=https\://services.gradle.org/distributions/gradle-4.9-bin.zip | ||
zipStoreBase=GRADLE_USER_HOME | ||
zipStorePath=wrapper/dists |
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 |
---|---|---|
@@ -1,59 +1,33 @@ | ||
package com.serjihsklovski.unicli; | ||
|
||
import com.serjihsklovski.unicli.annotation.Task; | ||
import com.serjihsklovski.unicli.annotation.Usage; | ||
import com.serjihsklovski.unicli.exception.AmbiguityException; | ||
import com.serjihsklovski.unicli.engine.lexer.ArgumentLexer; | ||
import com.serjihsklovski.unicli.engine.lexer.ArgumentLexerImpl; | ||
import com.serjihsklovski.unicli.engine.parser.ArgumentParser; | ||
import com.serjihsklovski.unicli.engine.parser.ArgumentParserImpl; | ||
import com.serjihsklovski.unicli.service.TaskService; | ||
import com.serjihsklovski.unicli.service.TaskServiceImpl; | ||
import com.serjihsklovski.unicli.service.UsageService; | ||
import com.serjihsklovski.unicli.service.UsageServiceImpl; | ||
import com.serjihsklovski.unicli.util.classprovider.ClassProvider; | ||
import com.serjihsklovski.unicli.util.classprovider.ClassProviderImpl; | ||
import com.serjihsklovski.unicli.util.runner.LazyActionsRunner; | ||
|
||
import java.lang.reflect.InvocationTargetException; | ||
import java.lang.reflect.Method; | ||
import java.util.Collections; | ||
import java.util.Set; | ||
import java.util.stream.Collectors; | ||
|
||
public class Unicli { | ||
|
||
// TODO: temporary implementation to enable single Task with its single Usage (v0.1.0) | ||
public static void run(String root, String... args) { | ||
if (args.length > 0) { | ||
throw new RuntimeException("Cannot parse arguments now :("); | ||
} | ||
ClassProvider classProvider = new ClassProviderImpl(); | ||
Set<Class> classPool = classProvider.fetchAllClassesByRoots(Collections.singleton(root)); | ||
|
||
TaskService taskService = new TaskServiceImpl(classPool); | ||
Set<Class> taskClasses = taskService.getAllTaskClasses().collect(Collectors.toSet()); | ||
if (taskClasses.size() == 0) { | ||
return; | ||
} | ||
if (taskClasses.size() > 1) { | ||
throw new AmbiguityException(String.format("Ambiguity: there are multiple classes annotated with `%s`.", | ||
Task.class.getCanonicalName())); | ||
} | ||
Class<?> taskClass = taskClasses.stream().findFirst().orElseThrow(RuntimeException::new); | ||
|
||
UsageService usageService = new UsageServiceImpl(); | ||
Set<Method> usageMethods = usageService.getAllUsagesByTaskClass(taskClass).collect(Collectors.toSet()); | ||
if (usageMethods.size() == 0) { | ||
return; | ||
} | ||
if (usageMethods.size() > 1) { | ||
throw new AmbiguityException(String.format("Ambiguity: there are multiple methods annotated with `%s`.", | ||
Usage.class.getCanonicalName())); | ||
} | ||
Method usageMethod = usageMethods.stream().findFirst().orElseThrow(RuntimeException::new); | ||
|
||
try { | ||
Object object = taskClass.newInstance(); | ||
usageMethod.invoke(object); | ||
} catch (InstantiationException | InvocationTargetException | IllegalAccessException e) { | ||
e.printStackTrace(); | ||
} | ||
|
||
ArgumentLexer lexer = new ArgumentLexerImpl(); | ||
ArgumentParser parser = new ArgumentParserImpl(taskService, usageService); | ||
|
||
LazyActionsRunner.run(parser.parseLexemes(lexer.getLexemes(args))); | ||
} | ||
|
||
} |
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
31 changes: 31 additions & 0 deletions
31
src/main/java/com/serjihsklovski/unicli/engine/lexer/ArgumentLexer.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,31 @@ | ||
package com.serjihsklovski.unicli.engine.lexer; | ||
|
||
import java.util.stream.Stream; | ||
|
||
/** | ||
* Argument lexer makes assumptions about what type | ||
* of the JAR arguments are given into a program. | ||
* To do that, we use the following wrapper class: | ||
* @see com.serjihsklovski.unicli.engine.lexer.Lexeme | ||
* | ||
* In most cases, the argument lexer cannot accept | ||
* the exact type of the lexeme - this is a responsibility | ||
* of another processors (e.g. parsers, interpreters). | ||
* It also does not provide a stateful argument processing. | ||
* | ||
* You can find all the lexeme types in the following enumeration: | ||
* @see com.serjihsklovski.unicli.engine.lexer.Lexeme.LexemeType | ||
*/ | ||
public interface ArgumentLexer { | ||
|
||
/** | ||
* Makes an assumption for each argument in the | ||
* given array of arguments. | ||
* | ||
* @param args "raw" arguments coming from a program | ||
* @return stream of lexemes | ||
* @see com.serjihsklovski.unicli.engine.lexer.Lexeme | ||
*/ | ||
Stream<Lexeme> getLexemes(String... args); | ||
|
||
} |
42 changes: 42 additions & 0 deletions
42
src/main/java/com/serjihsklovski/unicli/engine/lexer/ArgumentLexerImpl.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,42 @@ | ||
package com.serjihsklovski.unicli.engine.lexer; | ||
|
||
import java.util.regex.Pattern; | ||
import java.util.stream.Stream; | ||
|
||
public class ArgumentLexerImpl implements ArgumentLexer { | ||
|
||
@Override | ||
public Stream<Lexeme> getLexemes(String... args) { | ||
return Stream.of(args) | ||
.map(arg -> { | ||
if (isValidTaskIdentifier(arg)) { | ||
return new Lexeme(Lexeme.LexemeType.TASK_NAME_OR_VALUE, arg); | ||
} else if (isOption(arg)) { | ||
return new Lexeme(Lexeme.LexemeType.OPTION_OR_VALUE, arg); | ||
} else if (isParameter(arg)) { | ||
return new Lexeme(Lexeme.LexemeType.PARAMETER_OR_VALUE, arg); | ||
} else if (isShortOptionParamList(arg)) { | ||
return new Lexeme(Lexeme.LexemeType.SHORTCUTS_OR_VALUE, arg); | ||
} else { | ||
return new Lexeme(Lexeme.LexemeType.VALUE, arg); | ||
} | ||
}); | ||
} | ||
|
||
private boolean isValidTaskIdentifier(String arg) { | ||
return Pattern.compile("^[a-zA-Z_](?:-?[a-zA-Z_\\d]+)*$").matcher(arg).matches(); | ||
} | ||
|
||
private boolean isOption(String arg) { | ||
return Pattern.compile("^--[a-zA-Z_](?:-?[a-zA-Z_\\d]+)*$").matcher(arg).matches(); | ||
} | ||
|
||
private boolean isParameter(String arg) { | ||
return Pattern.compile("^--[a-zA-Z_](?:-?[a-zA-Z_\\d]+)*=.*$").matcher(arg).matches(); | ||
} | ||
|
||
private boolean isShortOptionParamList(String arg) { | ||
return Pattern.compile("^-[a-zA-Z]+$").matcher(arg).matches(); | ||
} | ||
|
||
} |
83 changes: 83 additions & 0 deletions
83
src/main/java/com/serjihsklovski/unicli/engine/lexer/Lexeme.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,83 @@ | ||
package com.serjihsklovski.unicli.engine.lexer; | ||
|
||
/** | ||
* Represents an argument as a lexeme: `type` + `value`. | ||
*/ | ||
public class Lexeme { | ||
|
||
/** | ||
* If the argument matches some pattern, | ||
* the lexer can assign the type to it. | ||
* | ||
* @see com.serjihsklovski.unicli.engine.lexer.ArgumentLexer | ||
*/ | ||
public enum LexemeType { | ||
|
||
/** | ||
* Represents strings, numbers, paths, etc. | ||
* The lexer can assign this type to an argument | ||
* if it does not match any pattern. | ||
*/ | ||
VALUE, | ||
|
||
/** | ||
* Represents task names. Examples: | ||
* `install` | ||
* `httpGet` | ||
* `my-task` | ||
*/ | ||
TASK_NAME_OR_VALUE, | ||
|
||
/** | ||
* Represents options. Followed by a task, the options | ||
* are related to this exact task, configuring its | ||
* behaviour. Options start with 2 dashes. Examples: | ||
* `--verbose` | ||
* `--sync` | ||
*/ | ||
OPTION_OR_VALUE, | ||
|
||
/** | ||
* Represents parameters. Followed by a task, the parameters | ||
* are related to this exact task, setting its required and | ||
* optional parameters. Parameter lexeme consists of the parameter | ||
* name, assignment character, and argument value. Parameter names | ||
* start with 2 dashes. Examples: | ||
* `--file=/tmp/test` | ||
* `--level=4` | ||
* `--empty-value=` | ||
*/ | ||
PARAMETER_OR_VALUE, | ||
|
||
/** | ||
* Represents shortcuts. Usually, a shortcut is a one-character | ||
* synonym for a task option/parameter. Less often, a shortcut | ||
* can be self-sufficient, i.e. there may not be a full option or | ||
* parameter name at all. Many shortcuts can be joined into one | ||
* so-called shortcut list. The shortcut/shortcut list start | ||
* with a dash. Examples: | ||
* `-o` | ||
* `-xcf` | ||
*/ | ||
SHORTCUTS_OR_VALUE, | ||
|
||
} | ||
|
||
private final LexemeType type; | ||
|
||
private final String value; | ||
|
||
public Lexeme(LexemeType type, String value) { | ||
this.type = type; | ||
this.value = value; | ||
} | ||
|
||
public LexemeType getType() { | ||
return type; | ||
} | ||
|
||
public String getValue() { | ||
return value; | ||
} | ||
|
||
} |
Oops, something went wrong.