Skip to content
This repository was archived by the owner on Jun 3, 2025. It is now read-only.

Commit 45ab76e

Browse files
authored
Merge pull request #390 from testmycode/command-categories
Command categories
2 parents 6a2d639 + 69154a0 commit 45ab76e

File tree

12 files changed

+304
-119
lines changed

12 files changed

+304
-119
lines changed

src/main/java/fi/helsinki/cs/tmc/cli/Application.java

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313

1414
import org.apache.commons.cli.CommandLine;
1515
import org.apache.commons.cli.GnuParser;
16+
import org.apache.commons.cli.Option;
17+
import org.apache.commons.cli.OptionBuilder;
1618
import org.apache.commons.cli.Options;
1719
import org.apache.commons.cli.ParseException;
1820
import org.slf4j.Logger;
@@ -23,6 +25,7 @@
2325
import java.util.Date;
2426
import java.util.List;
2527
import java.util.Map;
28+
import java.util.Set;
2629

2730
/**
2831
* The application class for the program.
@@ -49,11 +52,26 @@ public Application(CliContext context) {
4952
this.context = context;
5053
this.io = context.getIo();
5154

52-
options.addOption("h", "help", false, "Display help information about tmc-cli");
55+
options.addOption(
56+
OptionBuilder.withLongOpt("help-all")
57+
.withDescription("Display all help information of tmc-cli")
58+
.create());
59+
options.addOption("h", "help", false, "Display help information");
5360
options.addOption("v", "version", false, "Give the version of the tmc-cli");
5461
options.addOption("u", "force-update", false, "Force the auto-update");
5562
options.addOption("d", "no-update", false, "Disable temporarily the auto-update");
5663

64+
Set<String> helpCategories = CommandFactory.getCommandCategories();
65+
for (String category : helpCategories) {
66+
if (category.equals("") || category.equals("hidden")) {
67+
continue;
68+
}
69+
options.addOption(
70+
OptionBuilder.withLongOpt("help-" + category)
71+
.withDescription("Display " + category + " help information")
72+
.create());
73+
}
74+
5775
//TODO implement the inTests as context.property
5876
if (!context.inTests()) {
5977
shutdownHandler = new ShutdownHandler(context.getIo());
@@ -103,6 +121,17 @@ private String[] parseArgs(String[] args) {
103121
return null;
104122
}
105123

124+
// handle help flags
125+
for (Option opt : line.getOptions()) {
126+
String helpPrefix = "help-";
127+
if (!opt.getLongOpt().startsWith(helpPrefix)) {
128+
continue;
129+
}
130+
131+
String helpCategory = opt.getLongOpt().substring(helpPrefix.length());
132+
runCommand("help", new String[] {helpCategory});
133+
return null;
134+
}
106135
if (showHelp) {
107136
// don't run the help sub-command with -h switch
108137
if (commandName.equals("help")) {

src/main/java/fi/helsinki/cs/tmc/cli/command/HelpCommand.java

Lines changed: 51 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,40 +17,82 @@
1717

1818
@Command(name = "help", desc = "List every command")
1919
public class HelpCommand extends AbstractCommand {
20+
2021
private int longestNameLength;
22+
private CliContext context;
2123
private Io io;
2224

25+
@Override
26+
public String[] getUsages() {
27+
return new String[] {"[category]"};
28+
}
29+
2330
@Override
2431
public void getOptions(Options options) {}
2532

2633
@Override
2734
public void run(CliContext context, CommandLine args) {
28-
Application app = context.getApp();
35+
this.context = context;
2936
this.io = context.getIo();
3037

38+
String category = handleArgs(args);
39+
if (category == null) {
40+
return;
41+
}
42+
3143
StringBuilder sb = new StringBuilder();
32-
sb.append("TMC commands:\n");
44+
if (category.equals("")) {
45+
sb.append("TMC commands:\n");
46+
} else {
47+
sb.append("TMC commands in ").append(category).append(":\n");
48+
}
3349

34-
List<String> commandStrings = getCommandStrings();
50+
List<String> commandStrings = getCommandStrings(category);
3551
Collections.sort(commandStrings);
3652
for (String commandString : commandStrings) {
3753
sb.append(commandString).append("\n");
3854
}
3955

40-
app.printHelp(sb.toString());
56+
context.getApp().printHelp(sb.toString());
4157
}
4258

43-
private List<String> getCommandStrings() {
59+
private String handleArgs(CommandLine args) {
60+
String[] stringArguments = args.getArgs();
61+
if (stringArguments.length > 1) {
62+
io.errorln("Too many arguments.");
63+
printUsage(context);
64+
return null;
65+
}
66+
String category = "";
67+
if (stringArguments.length == 1) {
68+
category = stringArguments[0];
69+
}
70+
if (category.equals("all")) {
71+
return category;
72+
}
73+
Set<String> helpCategories = CommandFactory.getCommandCategories();
74+
if (!helpCategories.contains(category)) {
75+
io.errorln("Unknown command category \"" + category + "\".");
76+
return null;
77+
}
78+
return category;
79+
}
80+
81+
private List<String> getCommandStrings(String category) {
4482
List<String> strings = new ArrayList<>();
45-
Set<Class<Command>> commands = CommandFactory.getCommands();
46-
commands.remove(castToCommandClass(ShellHelperCommand.class));
47-
commands.remove(castToCommandClass(DocumentCommand.class));
83+
List<Class<Command>> commands;
84+
if (category.equals("all")) {
85+
commands = CommandFactory.getCommands();
86+
} else {
87+
commands = CommandFactory.getCategoryCommands(category);
88+
}
4889

4990
longestNameLength = longestName(commands);
5091
for (Class<Command> commandClass : commands) {
5192
Command command = CommandFactory.getCommand(commandClass);
5293
strings.add(createCommandString(command));
5394
}
95+
longestNameLength = Math.max(longestNameLength, 8);
5496
return strings;
5597
}
5698

@@ -69,7 +111,7 @@ private String createCommandString(Command command) {
69111
return builder.toString();
70112
}
71113

72-
private int longestName(Set<Class<Command>> commandList) {
114+
private int longestName(List<Class<Command>> commandList) {
73115
int longest = 0;
74116
for (Class<Command> commandClass : commandList) {
75117
Command command = CommandFactory.getCommand(commandClass);
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package fi.helsinki.cs.tmc.cli.command.admin;
2+
3+
import fi.helsinki.cs.tmc.cli.core.AbstractCommand;
4+
import fi.helsinki.cs.tmc.cli.core.CliContext;
5+
import fi.helsinki.cs.tmc.cli.core.Command;
6+
import fi.helsinki.cs.tmc.cli.io.Io;
7+
8+
import org.apache.commons.cli.CommandLine;
9+
import org.apache.commons.cli.Options;
10+
import org.slf4j.Logger;
11+
import org.slf4j.LoggerFactory;
12+
13+
@Command(name = "admin", desc = "The test command")
14+
public class TestAdminCommand extends AbstractCommand {
15+
16+
private static final Logger logger = LoggerFactory.getLogger(TestAdminCommand.class);
17+
private Io io;
18+
19+
@Override
20+
public void getOptions(Options options) { }
21+
22+
@Override
23+
public void run(CliContext context, CommandLine args) {
24+
this.io = context.getIo();
25+
26+
io.println("test");
27+
}
28+
}

src/main/java/fi/helsinki/cs/tmc/cli/command/DocumentCommand.java renamed to src/main/java/fi/helsinki/cs/tmc/cli/command/hidden/DocumentCommand.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package fi.helsinki.cs.tmc.cli.command;
1+
package fi.helsinki.cs.tmc.cli.command.hidden;
22

33
import fi.helsinki.cs.tmc.cli.core.AbstractCommand;
44
import fi.helsinki.cs.tmc.cli.core.CliContext;

src/main/java/fi/helsinki/cs/tmc/cli/command/ShellHelperCommand.java renamed to src/main/java/fi/helsinki/cs/tmc/cli/command/hidden/ShellHelperCommand.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package fi.helsinki.cs.tmc.cli.command;
1+
package fi.helsinki.cs.tmc.cli.command.hidden;
22

33
import fi.helsinki.cs.tmc.cli.core.AbstractCommand;
44
import fi.helsinki.cs.tmc.cli.core.CliContext;

src/main/java/fi/helsinki/cs/tmc/cli/core/CommandAnnotationProcessor.java

Lines changed: 49 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,36 @@ public class CommandAnnotationProcessor extends AbstractProcessor {
2828

2929
private static final String CLASS_NAME = "CommandList";
3030
private static final String PACKAGE_NAME = "fi.helsinki.cs.tmc.cli.core";
31+
private static final String COMMAND_PACKAGE_NAME = "fi.helsinki.cs.tmc.cli.command";
3132
private static final String TAB = " ";
3233

34+
@Override
35+
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
36+
Map<String, String> map = new HashMap<>();
37+
38+
for (Element elem : roundEnv.getElementsAnnotatedWith(Command.class)) {
39+
if (elem.getKind() != ElementKind.CLASS) {
40+
logger.warn("Element with command annotation is not class: " + elem.toString());
41+
return false;
42+
}
43+
Command command = elem.getAnnotation(Command.class);
44+
logger.info("Element with annotation: " + elem.toString());
45+
logger.info("Element name with annotation: " + elem.getClass().getCanonicalName());
46+
47+
TypeElement classElement = (TypeElement) elem;
48+
map.put(
49+
command.name(),
50+
processingEnv.getElementUtils().getBinaryName(classElement).toString());
51+
}
52+
53+
try {
54+
generateSourceFile(map);
55+
} catch (IOException ex) {
56+
logger.warn("Failed to create source file." + ex);
57+
}
58+
return true;
59+
}
60+
3361
private void generateSourceFile(Map<String, String> map) throws IOException {
3462
JavaFileObject jfo =
3563
processingEnv.getFiler().createSourceFile(PACKAGE_NAME + "." + CLASS_NAME);
@@ -43,53 +71,42 @@ private void generateSourceFile(Map<String, String> map) throws IOException {
4371
for (Entry<String, String> entry : map.entrySet()) {
4472
bwriter.append("import ").append(entry.getValue()).append(";\n");
4573
}
46-
bwriter.append("//CHECKSTYLE:ON\n");
4774

4875
bwriter.append("\npublic class " + CLASS_NAME + " {\n");
49-
bwriter.append(TAB + "static {\n");
76+
bwriter.append(TAB + "public " + CLASS_NAME + "() {\n");
5077
for (Entry<String, String> entry : map.entrySet()) {
51-
String[] parts = entry.getValue().split("\\.");
52-
if (parts.length == 0) {
78+
String classPath = entry.getValue();
79+
String[] parts = classPath.split("\\.");
80+
if (parts.length < 2) {
5381
continue;
5482
}
55-
// print out the lines that add the commands to the command factory.
83+
5684
String className = parts[parts.length - 1];
85+
String packageName = "?";
86+
if (classPath.startsWith(COMMAND_PACKAGE_NAME)) {
87+
//remove class name and dot
88+
packageName = classPath.substring(
89+
0,
90+
classPath.length() - className.length() - 1);
91+
//remove prefix
92+
packageName = packageName.substring(COMMAND_PACKAGE_NAME.length());
93+
//remove the first dot
94+
if (packageName.startsWith(".")) {
95+
packageName = packageName.substring(1);
96+
}
97+
}
5798
bwriter.append(TAB + TAB + "CommandFactory.addCommand(\"")
5899
.append(entry.getKey())
100+
.append("\", \"")
101+
.append(packageName)
59102
.append("\", ")
60103
.append(className)
61104
.append(".class);\n");
62105
}
63106
bwriter.append(TAB + "}\n");
64107
bwriter.append("}\n");
108+
bwriter.append("//CHECKSTYLE:ON\n");
65109
bwriter.flush();
66110
}
67111
}
68-
69-
@Override
70-
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
71-
Map<String, String> map = new HashMap<>();
72-
73-
for (Element elem : roundEnv.getElementsAnnotatedWith(Command.class)) {
74-
if (elem.getKind() != ElementKind.CLASS) {
75-
logger.warn("Element with command annotation is not class: " + elem.toString());
76-
return false;
77-
}
78-
Command command = elem.getAnnotation(Command.class);
79-
logger.info("Element with annotation: " + elem.toString());
80-
logger.info("Element name with annotation: " + elem.getClass().getCanonicalName());
81-
82-
TypeElement classElement = (TypeElement) elem;
83-
map.put(
84-
command.name(),
85-
processingEnv.getElementUtils().getBinaryName(classElement).toString());
86-
}
87-
88-
try {
89-
generateSourceFile(map);
90-
} catch (IOException ex) {
91-
logger.warn("Failed to create source file." + ex);
92-
}
93-
return true;
94-
}
95112
}

0 commit comments

Comments
 (0)