Skip to content

Commit 1e25062

Browse files
authored
Merge pull request #143 from Schlaumeier5/136-improve-command-line-interface
Improved the command line interface
2 parents 0d77db6 + 3c9f52f commit 1e25062

File tree

7 files changed

+100
-67
lines changed

7 files changed

+100
-67
lines changed

build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ dependencies {
1919
implementation("com.google.code.gson:gson:2.13.1")
2020
implementation("commons-codec:commons-codec:1.19.0")
2121
implementation("com.googlecode.owasp-java-html-sanitizer:owasp-java-html-sanitizer:20240325.1")
22+
implementation("org.jline:jline:3.30.6") // for better console input handling
2223

2324
testImplementation("org.junit.jupiter:junit-jupiter:5.10.0") // using JUnit 5 (latest)
2425
}

src/main/java/de/igslandstuhl/database/Application.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,12 @@ public Topic[] readFile(String file) throws SerializationException, SQLException
9292

9393
public static void main(String[] args) throws Exception {
9494
instance = new Application(args);
95+
96+
if (!getInstance().suppressCmd()) {
97+
Command.registerCommands();
98+
CommandLineUtils.setup();
99+
}
100+
95101
Server.getInstance().getConnection().createTables();
96102

97103
Holiday.setupCurrentSchoolYear();
@@ -100,10 +106,6 @@ public static void main(String[] args) throws Exception {
100106
if (getInstance().runsWebServer()) {
101107
Server.getInstance().getWebServer().start();
102108
}
103-
if (!getInstance().suppressCmd()) {
104-
Command.registerCommands();
105-
CommandLineUtils.setup();
106-
}
107109

108110
while (true) {
109111
if (!getInstance().suppressCmd()) {

src/main/java/de/igslandstuhl/database/Registry.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,14 @@
66
import java.util.stream.Stream;
77

88
import de.igslandstuhl.database.server.commands.Command;
9+
import de.igslandstuhl.database.server.commands.CommandDescription;
910
import de.igslandstuhl.database.server.webserver.handlers.HttpHandler;
1011
import de.igslandstuhl.database.server.webserver.requests.APIPostRequest;
1112
import de.igslandstuhl.database.server.webserver.requests.GetRequest;
1213

1314
public class Registry<K, V> implements Closeable {
1415
private static final Registry<String,Command> COMMAND_REGISTRY = new Registry<>();
16+
private static final Registry<String,CommandDescription> COMMAND_DESCRIPTION_REGISTRY = new Registry<>();
1517
private static final Registry<String,HttpHandler<APIPostRequest>> POST_HANDLER_REGISTRY = new Registry<>();
1618
private static final Registry<String,HttpHandler<GetRequest>> GET_HANDLER_REGISTRY = new Registry<>();
1719
public static Registry<String,Command> commandRegistry() {
@@ -23,6 +25,9 @@ public static Registry<String, HttpHandler<APIPostRequest>> postRequestHandlerRe
2325
public static Registry<String, HttpHandler<GetRequest>> getRequestHandlerRegistry() {
2426
return GET_HANDLER_REGISTRY;
2527
}
28+
public static Registry<String, CommandDescription> commandDescriptionRegistry() {
29+
return COMMAND_DESCRIPTION_REGISTRY;
30+
}
2631

2732
private final Map<K,V> objects = new HashMap<>();
2833

src/main/java/de/igslandstuhl/database/server/commands/Command.java

Lines changed: 43 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import java.sql.SQLException;
44
import java.util.Arrays;
5+
import java.util.List;
56
import java.util.Random;
67

78
import de.igslandstuhl.database.Registry;
@@ -12,6 +13,11 @@
1213
@FunctionalInterface
1314
public interface Command {
1415
public String execute(String[] args);
16+
public default CommandDescription getDescription() {
17+
return Registry.commandDescriptionRegistry().get(Registry.commandDescriptionRegistry()
18+
.keyStream().filter(k -> Registry.commandRegistry().get(k) == this)
19+
.findAny().orElse(null));
20+
}
1521

1622
public static String executeCommand(String command, String[] args) {
1723
try {
@@ -23,18 +29,19 @@ public static String executeCommand(String command, String[] args) {
2329
return "";
2430
}
2531
}
26-
public static void registerCommand(String name, Command command) {
32+
public static void registerCommand(String name, Command command, CommandDescription description) {
2733
Registry.commandRegistry().register(name, command);
34+
Registry.commandDescriptionRegistry().register(name, description);
2835
}
2936
public static void registerCommands() {
3037
registerCommand("exit", (args) -> {
3138
System.out.println("Exiting...");
3239
System.exit(0);
3340
return "";
34-
});
41+
}, new CommandDescription("exit", "Exits the application", "exit"));
3542
registerCommand("help", (args) -> {
36-
return Registry.commandRegistry().keyStream().reduce("Available Commands:", (s1,s2) -> s1+"\n"+s2);
37-
});
43+
return Registry.commandRegistry().keyStream().reduce("Available Commands:", (s1,s2) -> s1+"\n"+Registry.commandDescriptionRegistry().get(s2).toString());
44+
}, new CommandDescription("help", "Displays this help message", "help"));
3845
// Add admin
3946
registerCommand("add-admin", (args) -> {
4047
if (args.length < 2) return "Usage: add-admin [username] [password]";
@@ -46,7 +53,7 @@ public static void registerCommands() {
4653
return "Error while trying to add admin:\n" + CommonUtils.getStacktrace(e);
4754
}
4855
return "Successfully added admin";
49-
});
56+
}, new CommandDescription("add-admin", "Adds a new admin", "add-admin [username] [password]"));
5057
registerCommand("remove-admin", (args) -> {
5158
if (args.length < 1) return "Usage: remove-admin [username]";
5259
try {
@@ -57,7 +64,7 @@ public static void registerCommands() {
5764
return "Error while trying to remove admin:\n" + CommonUtils.getStacktrace(e);
5865
}
5966
return "Successfully removed admin";
60-
});
67+
}, new CommandDescription("remove-admin", "Removes an admin", "remove-admin [username]"));
6168
registerCommand("get-level-ratio", (args) -> {
6269
if (args.length < 1) return "Usage: get-level-ratio [level]";
6370
TaskLevel level;
@@ -69,7 +76,7 @@ public static void registerCommands() {
6976
return e.getMessage();
7077
}
7178
return String.valueOf(level.getRatio() * 100) + "%";
72-
});
79+
}, new CommandDescription("get-level-ratio", "Gets the ratio of a task level", "get-level-ratio [level]"));
7380

7481
// Room commands
7582
registerCommand("list-rooms", (args) -> {
@@ -79,13 +86,13 @@ public static void registerCommands() {
7986
return "Error while trying to access database:\n" + CommonUtils.getStacktrace(e);
8087
}
8188
return Room.getRooms().keySet().stream().reduce("Rooms:", (s1, s2) -> s1 + "\n" + s2);
82-
});
89+
}, new CommandDescription("list-rooms", "Lists all available rooms", "list-rooms"));
8390
registerCommand("get-room-level", (args) -> {
8491
if (args.length < 1) return "Usage: get-room-level [room]";
8592
Room room = Room.getRoom(argsPart(args, 0, args.length));
8693
if (room == null) return "Room not found. Try list-rooms for a list of available rooms";
8794
return "Room " + room.getLabel() + " has access level " + room.getMinimumLevel();
88-
});
95+
}, new CommandDescription("get-room-level", "Gets the minimum level required to access a room", "get-room-level [room]"));
8996
registerCommand("set-room-level", (args) -> {
9097
if (args.length < 2) return "Usage: set-room-level [room] [level]";
9198
Room room = Room.getRoom(argsPart(args, 0, args.length-1));
@@ -104,7 +111,7 @@ public static void registerCommands() {
104111
return e.getMessage();
105112
}
106113
return "Successfully changed room level";
107-
});
114+
}, new CommandDescription("set-room-level", "Sets the minimum level required to access a room", "set-room-level [room] [level]"));
108115
registerCommand("add-room", (args) -> {
109116
if (args.length < 2) return "Usage: add-room [room] [level]";
110117
if (Room.getRoom(args[0]) != null) return "Room already present";
@@ -122,7 +129,7 @@ public static void registerCommands() {
122129
throw new IllegalStateException(e);
123130
}
124131
return "Room successfully added";
125-
});
132+
}, new CommandDescription("add-room", "Adds a new room", "add-room [room] [level]"));
126133
registerCommand("remove-room", (args) -> {
127134
if (args.length < 1) return "Usage: remove-room [room]";
128135

@@ -133,13 +140,13 @@ public static void registerCommands() {
133140
return "Error while trying to access database: \n" + CommonUtils.getStacktrace(e);
134141
}
135142
return "Room successfully deleted";
136-
});
143+
}, new CommandDescription("remove-room", "Removes a room", "remove-room [room]"));
137144

138145

139146
// class commands
140147
registerCommand("list-classes", (args) -> {
141148
return SchoolClass.getAll().stream().map(SchoolClass::getLabel).reduce("Classes:", (s1,s2) -> s1 + "\n" + s2);
142-
});
149+
}, new CommandDescription("list-classes", "Lists all classes", "list-classes"));
143150
registerCommand("add-subject-to-class", (args) -> {
144151
if (args.length != 2) return "Usage: add-subject-to-class [subject] [class]";
145152

@@ -155,7 +162,7 @@ public static void registerCommands() {
155162
throw new IllegalStateException(e);
156163
}
157164
return "Successfully added subject to class";
158-
});
165+
}, new CommandDescription("add-subject-to-class", "Adds a subject to a class", "add-subject-to-class [subject] [class]"));
159166
registerCommand(("set-class-label"), (args) -> {
160167
if (args.length != 2) return "Usage: set-class-label [old] [new]";
161168

@@ -170,7 +177,7 @@ public static void registerCommands() {
170177
}
171178

172179
return "Class label successfully updated.";
173-
});
180+
}, new CommandDescription("set-class-label", "Sets the label of a class", "set-class-label [old] [new]"));
174181
registerCommand(("set-class-grade"), (args) -> {
175182
if (args.length != 2) return "Usage: set-class-grade [class] [grade]";
176183

@@ -191,12 +198,12 @@ public static void registerCommands() {
191198
}
192199

193200
return "Class grade successfully updated.";
194-
});
201+
}, new CommandDescription("set-class-grade", "Sets the grade of a class", "set-class-grade [class] [grade]"));
195202
registerCommand("add-class", (args) -> {
196203
if (args.length != 1) return "Usage: add-class [class]";
197204
SchoolClass schoolClass = SchoolClass.getOrCreate(args[0]);
198205
return "Successfully created class " + schoolClass.getLabel() + " of grade " + schoolClass.getGrade();
199-
});
206+
}, new CommandDescription("add-class", "Adds a new class", "add-class [class]"));
200207
registerCommand("remove-class", (args) -> {
201208
if (args.length != 1) return "Usage: remove-class [class]";
202209
SchoolClass schoolClass = SchoolClass.get(args[0]);
@@ -207,21 +214,21 @@ public static void registerCommands() {
207214
throw new IllegalStateException(e);
208215
}
209216
return "Successfully deleted class";
210-
});
217+
}, new CommandDescription("remove-class", "Removes a class", "remove-class [class]"));
211218
// school year
212219
registerCommand("list-school-years", (args) -> {
213220
return SchoolYear.getAll().stream().map(SchoolYear::getLabel).reduce("School years:", (s1,s2) -> s1 + "\n" + s2);
214-
});
221+
}, new CommandDescription("list-school-years", "Lists all school years", "list-school-years"));
215222
registerCommand("get-current-school-year", (args) -> {
216223
SchoolYear current = SchoolYear.getCurrentYear();
217224
if (current == null) return "No school year found.";
218225
return "Current school year: " + current.getLabel() + ", current week: " + current.getCurrentWeek() + ", total week count: " + current.getWeekCount();
219-
});
226+
}, new CommandDescription("get-current-school-year", "Gets the current school year", "get-current-school-year"));
220227
registerCommand("get-current-week", (args) -> {
221228
SchoolYear current = SchoolYear.getCurrentYear();
222229
if (current == null) return "No school year found.";
223230
return "Current week: " + current.getCurrentWeek();
224-
});
231+
}, new CommandDescription("get-current-week", "Gets the current week", "get-current-week"));
225232
registerCommand("set-current-week", (args) -> {
226233
if (args.length != 1) return "Usage: set-current-week [week]";
227234
int week;
@@ -239,7 +246,7 @@ public static void registerCommands() {
239246
throw new IllegalStateException(e);
240247
}
241248
return "Week successfully changed";
242-
});
249+
}, new CommandDescription("set-current-week", "Sets the current week", "set-current-week [week]"));
243250
registerCommand("inc-week", (args) -> {
244251
SchoolYear current = SchoolYear.getCurrentYear();
245252
if (current == null) return "No school year found";
@@ -249,7 +256,7 @@ public static void registerCommands() {
249256
throw new IllegalStateException(e);
250257
}
251258
return "Week successfully changed";
252-
});
259+
}, new CommandDescription("inc-week", "Increases the current week by 1", "inc-week"));
253260
registerCommand("add-school-year", (args) -> {
254261
if (args.length != 2) return "Usage: add-school-year [label] [week count]";
255262
try {
@@ -260,7 +267,7 @@ public static void registerCommands() {
260267
throw new IllegalStateException(e);
261268
}
262269
return "Successfully added school year";
263-
});
270+
}, new CommandDescription("add-school-year", "Adds a new school year", "add-school-year [label] [week count]"));
264271
registerCommand("remove-school-year", (args) -> {
265272
if (args.length != 1) return "Usage: remove-school-year [label]";
266273
SchoolYear schoolYear = SchoolYear.get(args[0]);
@@ -271,7 +278,7 @@ public static void registerCommands() {
271278
throw new IllegalStateException(e);
272279
}
273280
return "School Year successfully removed";
274-
});
281+
}, new CommandDescription("remove-school-year", "Removes a school year", "remove-school-year [label]"));
275282
// User commands
276283
registerCommand("regenerate-user-password", (args) -> {
277284
if (args.length != 1) return "Usage: regenerate-user-password [user]";
@@ -282,10 +289,10 @@ public static void registerCommands() {
282289
} catch (SQLException e) {
283290
throw new IllegalStateException(e);
284291
}
285-
});
292+
}, new CommandDescription("regenerate-user-password", "Regenerates a user's password", "regenerate-user-password [user]"));
286293
registerCommand("generate-password", (args) -> {
287294
return "Passwort: " + User.generateRandomPassword(12, CommonUtils.stringToSeed(argsPart(args, 0, args.length)) + System.currentTimeMillis() + new Random().nextInt(1000));
288-
});
295+
}, new CommandDescription("generate-password", "Generates a random password", "generate-password [optional seed]"));
289296
registerCommand("change-user-password", (args) -> {
290297
if (args.length != 2) return "Usage: change-user-password [user] [password]";
291298
User user = User.getUser(args[0]);
@@ -296,11 +303,11 @@ public static void registerCommands() {
296303
} catch (SQLException e) {
297304
throw new IllegalStateException(e);
298305
}
299-
});
306+
}, new CommandDescription("change-user-password", "Changes a user's password", "change-user-password [user] [password]"));
300307
// Subject commands
301308
registerCommand("list-subjects", (args) -> {
302309
return Subject.getAll().stream().map(Subject::getName).reduce("Subjects:", (s1,s2) -> s1+"\n"+s2);
303-
});
310+
}, new CommandDescription("list-subjects", "Lists all subjects", "list-subjects"));
304311
registerCommand("add-subject", (args) -> {
305312
if (args.length < 1) return "Usage: add-subject [subject]";
306313
String name = argsPart(args, 0, args.length);
@@ -310,7 +317,7 @@ public static void registerCommands() {
310317
} catch (SQLException e) {
311318
throw new IllegalStateException(e);
312319
}
313-
});
320+
}, new CommandDescription("add-subject", "Adds a new subject", "add-subject [subject]"));
314321
registerCommand("rename-subject", (args) -> {
315322
if (args.length < 3) return "Usage: rename-subject [old name] : [new name]";
316323

@@ -327,7 +334,7 @@ public static void registerCommands() {
327334
} catch (SQLException e) {
328335
throw new IllegalStateException(e);
329336
}
330-
});
337+
}, new CommandDescription("rename-subject", "Renames a subject", "rename-subject [old name] : [new name]"));
331338
registerCommand("remove-subject", (args) -> {
332339
if (args.length == 0) return "remove-subject [name]";
333340
String name = argsPart(args, 0, args.length);
@@ -339,7 +346,7 @@ public static void registerCommands() {
339346
} catch (SQLException e) {
340347
throw new IllegalStateException(e);
341348
}
342-
});
349+
}, new CommandDescription("remove-subject", "Removes a subject", "remove-subject [name]"));
343350
// manual sql commands
344351
registerCommand("sql-update", (args) -> {
345352
String command = argsPart(args, 0, args.length);
@@ -349,9 +356,12 @@ public static void registerCommands() {
349356
throw new IllegalArgumentException(e);
350357
}
351358
return "Successfully executed";
352-
});
359+
}, new CommandDescription("sql-update", "Executes an update sql command", "sql-update [command]"));
353360
}
354361
private static String argsPart(String[] args, int start, int end) {
355362
return Arrays.stream(Arrays.copyOfRange(args, start, end)).reduce("", (s1,s2) -> s1+" "+s2).replaceFirst(" ", "");
356363
}
364+
public static List<String> getAllCommandNames() {
365+
return Registry.commandRegistry().keyStream().toList();
366+
}
357367
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package de.igslandstuhl.database.server.commands;
2+
3+
import de.igslandstuhl.database.utils.StringUtils;
4+
5+
public record CommandDescription(String name, String description, String usage) {
6+
public String toString() {
7+
return StringUtils.fitSize(name, 20) + "\t" + StringUtils.fitSize(description, 50) + "\t" + StringUtils.fitSize(usage, 30);
8+
}
9+
}

0 commit comments

Comments
 (0)