Skip to content

Commit 61a194e

Browse files
committed
Implement command handling system with CommandDispatcher, CommandRegistry, and CommandNode classes
1 parent 946e5e3 commit 61a194e

10 files changed

Lines changed: 533 additions & 103 deletions

File tree

src/main/java/io/nodelink/server/NodeLink.java

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,33 @@
33
import io.nodelink.server.log.Logger;
44
import io.nodelink.server.update.Updater;
55

6+
import java.io.File;
7+
68
public class NodeLink extends NodeLinkHelper {
79

810
private static final NodeLink INSTANCE = new NodeLink();
911

1012
static void main(String[] args) {
1113
try {
12-
NodeLink.getInstance().getUpdater().handleArguments(args);
13-
NodeLink.getInstance().getUpdater().checkForUpdates().block();
14+
NodeLink.getInstance().getUpdater().checkForUpdates();
15+
16+
for (int i = 0; i < args.length; i++) {
17+
if ("--delete-old".equals(args[i]) && (i + 1) < args.length) {
18+
String oldJarPath = args[i + 1];
19+
File oldFile = new File(oldJarPath);
20+
Thread.sleep(2000);
21+
22+
if (oldFile.exists()) {
23+
boolean deleted = oldFile.delete();
24+
if (!deleted) {
25+
System.err.println("Failed to delete old JAR: " + oldJarPath);
26+
} else {
27+
System.out.println("Old JAR deleted successfully: " + oldJarPath);
28+
}
29+
}
30+
break;
31+
}
32+
}
1433

1534
} catch (Exception e) {
1635
getInstance().getLogger().ERROR("Error deleting old JAR: " + e.getMessage());

src/main/java/io/nodelink/server/NodeLinkHelper.java

Lines changed: 61 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,21 @@
11
package io.nodelink.server;
22

3+
import io.nodelink.server.command.CommandDispatcher;
4+
import io.nodelink.server.command.CommandLogics;
5+
import io.nodelink.server.command.CommandRegistry;
6+
import io.nodelink.server.command.TabCompleter;
7+
import io.nodelink.server.enums.CommandsEnum;
38
import io.nodelink.server.update.Version;
4-
import org.jline.reader.EndOfFileException;
5-
import org.jline.reader.UserInterruptException;
9+
10+
import org.jline.reader.*;
611
import org.jline.terminal.Terminal;
712
import org.jline.terminal.TerminalBuilder;
8-
import org.jline.reader.LineReader;
9-
import org.jline.reader.LineReaderBuilder;
1013
import org.jline.utils.InfoCmp.Capability;
14+
1115
import java.nio.file.Paths;
1216

17+
import java.util.List;
18+
1319
public class NodeLinkHelper {
1420

1521
private static final NodeLinkHelper INSTANCE = new NodeLinkHelper();
@@ -21,9 +27,24 @@ public class NodeLinkHelper {
2127
private static final String WHITE = "\u001B[37m";
2228
private static final String YELLOW = "\u001B[33m";
2329

24-
private static String STATUS = "Main";
30+
2531
private static String PRODUCT = "Post Production";
2632

33+
private Terminal terminal;
34+
35+
private String STATUS = "OFFLINE";
36+
37+
public synchronized void updateStatus(String newStatus) {
38+
this.STATUS = (newStatus == null) ? "" : newStatus;
39+
if (this.terminal != null) {
40+
fullClearAndRefresh(this.terminal);
41+
}
42+
}
43+
44+
public synchronized String getStatus() {
45+
return this.STATUS;
46+
}
47+
2748
private static final int RESERVED_ROWS = 36;
2849

2950
public static NodeLinkHelper getHelper() {
@@ -36,13 +57,29 @@ public void INITIALIZE() {
3657

3758
private void initTerminal() {
3859
try {
39-
Terminal terminal = TerminalBuilder.builder()
60+
terminal = TerminalBuilder.builder()
4061
.name("NodeLink Server")
4162
.system(true)
4263
.build();
4364

65+
CommandRegistry registry = new CommandRegistry();
66+
registry.registerParentEnum(CommandsEnum.class);
67+
68+
TabCompleter appCompleter = new TabCompleter(registry);
69+
70+
CommandDispatcher dispatcher = new CommandDispatcher(registry);
71+
72+
CommandLogics logics = new CommandLogics(dispatcher, terminal);
73+
4474
LineReader reader = LineReaderBuilder.builder()
4575
.terminal(terminal)
76+
.completer((lineReader, parsedLine, candidates) -> {
77+
String buffer = parsedLine.line();
78+
List<String> suggestions = appCompleter.complete(buffer);
79+
for (String s : suggestions) {
80+
candidates.add(new Candidate(s));
81+
}
82+
})
4683
.variable(LineReader.HISTORY_FILE, Paths.get("bin/history.txt"))
4784
.option(LineReader.Option.AUTO_FRESH_LINE, true)
4885
.build();
@@ -55,26 +92,28 @@ private void initTerminal() {
5592
try {
5693
String command = reader.readLine(prompt);
5794

58-
if (command == null || command.equalsIgnoreCase("exit")) System.exit(1);
95+
if (command == null || command.equalsIgnoreCase("exit" ) || command.equalsIgnoreCase("quit")) {
96+
System.exit(1);
97+
}
5998

6099
if (command.equalsIgnoreCase("clear")) {
61100
fullClearAndRefresh(terminal);
62101
continue;
63102
}
64103

65-
if (command.equalsIgnoreCase("wow")) {
66-
STATUS = "Wow Mode";
67-
fullClearAndRefresh(terminal);
68-
continue;
104+
boolean handled = dispatcher.dispatch(command);
105+
if (!handled) {
106+
terminal.writer().println("Commande inconnue : " + command);
107+
terminal.flush();
69108
}
70109

71-
terminal.writer().println("Exécution de : " + command);
72-
73110
} catch (UserInterruptException | EndOfFileException e) {
74-
break;
111+
System.exit(1);
75112
}
76113
}
77-
} catch (Exception e) {}
114+
} catch (Exception e) {
115+
e.printStackTrace();
116+
}
78117
}
79118

80119
private void fullClearAndRefresh(Terminal terminal) {
@@ -164,12 +203,12 @@ private String LOGO() {
164203

165204
private String HEADER() {
166205
return """
167-
__ __ __ ____ __ ______ ____ __ __ ______ \s
168-
/\\ \\/\\ \\ /\\ \\ /\\ _`\\ /\\ \\ /\\__ _\\ /\\ _`\\ /\\ \\/\\ \\ /\\__ _\\ \s
169-
\\ \\ `\\\\ \\ \\ \\ \\ \\ \\ \\/\\_\\ \\ \\ \\ \\/_/\\ \\/ \\ \\ \\L\\_\\ \\ \\ `\\\\ \\ \\/_/\\ \\/ \s
170-
\\ \\ , ` \\ \\ \\ \\ __ _______ \\ \\ \\/_/_ \\ \\ \\ __ \\ \\ \\ \\ \\ _\\L \\ \\ , ` \\ \\ \\ \\ \s
171-
\\ \\ \\`\\ \\ \\ \\ \\L\\ \\ /\\______\\ \\ \\ \\L\\ \\ \\ \\ \\L\\ \\ \\_\\ \\__ \\ \\ \\L\\ \\ \\ \\ \\`\\ \\ \\ \\ \\\s
172-
\\ \\_\\ \\_\\ \\ \\____/ \\/______/ \\ \\____/ \\ \\____/ /\\_____\\ \\ \\____/ \\ \\_\\ \\_\\ \\ \\_\\
173-
\\/_/\\/_/ \\/___/ \\/___/ \\/___/ \\/_____/ \\/___/ \\/_/\\/_/ \\/_/""";
206+
__ __ __ ____ ____ ____ __ __ ____ ____ \s
207+
/\\ \\/\\ \\ /\\ \\ /\\ _`\\ /\\ _`\\ /\\ _`\\ /\\ \\/\\ \\ /\\ _`\\ /\\ _`\\ \s
208+
\\ \\ `\\\\ \\ \\ \\ \\ \\ \\,\\L\\_\\ \\ \\ \\L\\_\\ \\ \\ \\L\\ \\ \\ \\ \\ \\ \\ \\ \\ \\L\\_\\ \\ \\ \\L\\ \\ \s
209+
\\ \\ , ` \\ \\ \\ \\ __ _______ \\/_\\__ \\ \\ \\ _\\L \\ \\ , / \\ \\ \\ \\ \\ \\ \\ _\\L \\ \\ , / \s
210+
\\ \\ \\`\\ \\ \\ \\ \\L\\ \\ /\\______\\ /\\ \\L\\ \\ \\ \\ \\L\\ \\ \\ \\ \\\\ \\ \\ \\ \\_/ \\ \\ \\ \\L\\ \\ \\ \\ \\\\ \\ \s
211+
\\ \\_\\ \\_\\ \\ \\____/ \\/______/ \\ `\\____\\ \\ \\____/ \\ \\_\\ \\_\\ \\ `\\___/ \\ \\____/ \\ \\_\\ \\_\\
212+
\\/_/\\/_/ \\/___/ \\/_____/ \\/___/ \\/_/\\/ / `\\/__/ \\/___/ \\/_/\\/ /""";
174213
}
175214
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package io.nodelink.server.command;
2+
3+
import java.util.*;
4+
import java.util.function.Consumer;
5+
6+
/**
7+
* Dispatcher simple : associe un Enum (handler key) à une action (Consumer<String[]>).
8+
* Lors de l'exécution, on trouve le noeud le plus profond et on invoque le handler enregistré pour son owner.
9+
*/
10+
public class CommandDispatcher {
11+
12+
private final CommandRegistry registry;
13+
private final Map<Enum<?>, Consumer<String[]>> handlers = new HashMap<>();
14+
15+
public CommandDispatcher(CommandRegistry registry) {
16+
this.registry = registry;
17+
}
18+
19+
public void registerHandler(Enum<?> key, Consumer<String[]> action) {
20+
handlers.put(key, action);
21+
}
22+
23+
public boolean dispatch(String inputLine) {
24+
if (inputLine == null || inputLine.isBlank()) return false;
25+
List<String> tokens = Arrays.asList(inputLine.trim().split("\\s+"));
26+
Map.Entry<CommandNode, Integer> found = registry.findDeepest(tokens);
27+
CommandNode node = found.getKey();
28+
if (node == null) return false;
29+
Enum<?> owner = node.getOwner();
30+
if (owner == null) return false;
31+
Consumer<String[]> action = handlers.get(owner);
32+
if (action == null) return false;
33+
action.accept(tokens.toArray(new String[0]));
34+
return true;
35+
}
36+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package io.nodelink.server.command;
2+
3+
import io.nodelink.server.NodeLink;
4+
import io.nodelink.server.enums.CommandsEnum;
5+
import org.jline.terminal.Terminal;
6+
7+
public class CommandLogics {
8+
9+
public CommandLogics(CommandDispatcher dispatcher, Terminal terminal) {
10+
dispatcher.registerHandler(CommandsEnum.SERVICE_SET_CLUSTER, tokens -> {
11+
terminal.writer().println("Mode cluster activé");
12+
13+
NodeLink.getHelper().updateStatus("Cluster");
14+
});
15+
16+
dispatcher.registerHandler(CommandsEnum.SERVICE_SET_BONE, tokens -> {
17+
terminal.writer().println("Mode bone activé");
18+
19+
NodeLink.getHelper().updateStatus("Bone");
20+
});
21+
}
22+
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
package io.nodelink.server.command;
2+
3+
import java.util.*;
4+
5+
public class CommandNode {
6+
private final String name;
7+
private final LinkedHashMap<String, CommandNode> children = new LinkedHashMap<>();
8+
private Enum<?> owner;
9+
10+
public CommandNode(String name) {
11+
this.name = name;
12+
}
13+
14+
public String getName() {
15+
return name;
16+
}
17+
18+
public CommandNode addChild(CommandNode child) {
19+
children.put(child.name, child);
20+
return this;
21+
}
22+
23+
public CommandNode addChild(String childName, CommandNode child) {
24+
children.put(childName, child);
25+
return this;
26+
}
27+
28+
public CommandNode child(String childName) {
29+
return children.computeIfAbsent(childName, CommandNode::new);
30+
}
31+
32+
public Collection<CommandNode> getChildren() {
33+
return children.values();
34+
}
35+
36+
public Set<String> getChildNames() {
37+
return children.keySet();
38+
}
39+
40+
public CommandNode getChild(String name) {
41+
return children.get(name);
42+
}
43+
44+
public boolean hasChildren() {
45+
return !children.isEmpty();
46+
}
47+
48+
public void setOwner(Enum<?> owner) {
49+
this.owner = owner;
50+
}
51+
52+
public Enum<?> getOwner() {
53+
return owner;
54+
}
55+
56+
@Override
57+
public String toString() {
58+
return "CommandNode{" + name + ", children=" + children.keySet() + "}";
59+
}
60+
}
61+
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
package io.nodelink.server.command;
2+
3+
import io.nodelink.server.provider.CommandsProvider;
4+
5+
import java.util.*;
6+
7+
/**
8+
* Registre central des commandes.
9+
* registerParentEnum fusionne désormais les arbres fournis par plusieurs constantes
10+
* du même enum (utile si vous définissez des sous-arbres dans le même enum ParentCommands).
11+
*/
12+
public class CommandRegistry {
13+
14+
private final Map<String, CommandNode> roots = new LinkedHashMap<>();
15+
16+
public void registerParentEnum(Class<? extends Enum<?>> enumClass) {
17+
if (enumClass == null) return;
18+
Enum<?>[] consts = enumClass.getEnumConstants();
19+
if (consts == null) return;
20+
for (Enum<?> e : consts) {
21+
if (e instanceof CommandsProvider cpp) {
22+
CommandNode node = cpp.getCommandNode();
23+
if (node != null) {
24+
// fusionner l'arbre partiel sous la racine node.getName()
25+
CommandNode root = roots.computeIfAbsent(node.getName(), CommandNode::new);
26+
merge(root, node);
27+
// si la racine n'a pas de owner et que la constante fournit un owner sur la racine, conserver
28+
if (root.getOwner() == null && node.getOwner() != null) {
29+
root.setOwner(node.getOwner());
30+
}
31+
}
32+
}
33+
}
34+
}
35+
36+
private void merge(CommandNode target, CommandNode src) {
37+
// si src a un owner et target n'en a pas, propager l'owner du noeud racine de src
38+
if (target.getOwner() == null && src.getOwner() != null) {
39+
target.setOwner(src.getOwner());
40+
}
41+
for (CommandNode child : src.getChildren()) {
42+
CommandNode existing = target.getChild(child.getName());
43+
if (existing == null) {
44+
// cloner l'arbre child dans target
45+
target.addChild(child);
46+
} else {
47+
// merger récursivement
48+
merge(existing, child);
49+
}
50+
}
51+
}
52+
53+
public Optional<CommandNode> getRoot(String name) {
54+
return Optional.ofNullable(roots.get(name));
55+
}
56+
57+
public Collection<CommandNode> getRoots() {
58+
return roots.values();
59+
}
60+
61+
public Map.Entry<CommandNode, Integer> findDeepest(List<String> tokens) {
62+
if (tokens == null || tokens.isEmpty()) return Map.entry(null, -1);
63+
CommandNode current = roots.get(tokens.get(0));
64+
if (current == null) return Map.entry(null, -1);
65+
int idx = 0;
66+
while (idx + 1 < tokens.size()) {
67+
CommandNode next = current.getChild(tokens.get(idx + 1));
68+
if (next == null) break;
69+
current = next;
70+
idx++;
71+
}
72+
return Map.entry(current, idx);
73+
}
74+
}

0 commit comments

Comments
 (0)