1 | package com.github.netkorp.telegram.framework.managers; |
||
0 ignored issues
–
show
Code Smell
introduced
by
![]() |
|||
2 | |||
3 | import com.github.netkorp.telegram.framework.annotations.TelegramCommand; |
||
4 | import com.github.netkorp.telegram.framework.commands.interfaces.Command; |
||
5 | import com.github.netkorp.telegram.framework.commands.interfaces.HelpCommand; |
||
6 | import com.github.netkorp.telegram.framework.commands.interfaces.MultistageCommand; |
||
7 | import com.github.netkorp.telegram.framework.commands.multistage.MultistageCloseCommand; |
||
8 | import com.github.netkorp.telegram.framework.commands.multistage.MultistageDoneCommand; |
||
9 | import com.github.netkorp.telegram.framework.properties.CommandProperties; |
||
10 | import com.github.netkorp.telegram.framework.exceptions.CommandNotActive; |
||
11 | import com.github.netkorp.telegram.framework.exceptions.CommandNotFound; |
||
12 | import org.slf4j.Logger; |
||
13 | import org.slf4j.LoggerFactory; |
||
14 | import org.springframework.beans.factory.annotation.Autowired; |
||
15 | import org.springframework.stereotype.Component; |
||
16 | |||
17 | import java.lang.invoke.MethodHandles; |
||
18 | import java.util.HashMap; |
||
19 | import java.util.LinkedList; |
||
20 | import java.util.List; |
||
21 | import java.util.Map; |
||
22 | import java.util.Optional; |
||
23 | |||
24 | /** |
||
25 | * Provides the component for managing all of the commands available in the bot. |
||
26 | * It includes the management of non-secure commands, active command, basic commands and |
||
27 | * those commands that are involved in the multistage command flow. |
||
28 | */ |
||
29 | @SuppressWarnings("WeakerAccess") |
||
0 ignored issues
–
show
|
|||
30 | @Component |
||
31 | public class CommandManager { |
||
0 ignored issues
–
show
|
|||
32 | |||
33 | private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); |
||
0 ignored issues
–
show
|
|||
34 | |||
35 | /** |
||
36 | * The list of the available commands into a map to get a quick access from its name. |
||
37 | */ |
||
38 | private final Map<String, Command> commandsByFullName; |
||
39 | |||
40 | /** |
||
41 | * The list of the available commands. |
||
42 | */ |
||
43 | private final List<Command> commands; |
||
44 | |||
45 | /** |
||
46 | * The list of the non-secure commands. |
||
47 | */ |
||
48 | private final List<Command> nonSecureCommands; |
||
49 | |||
50 | /** |
||
51 | * The command that is active for each user. |
||
52 | */ |
||
53 | private final Map<Long, MultistageCommand> activeCommand; |
||
54 | |||
55 | /** |
||
56 | * The properties of the commands. |
||
57 | */ |
||
58 | private final CommandProperties commandProperties; |
||
59 | |||
60 | /** |
||
61 | * The command that closes an active conversation with the bot, indicating to the active command that the conversation is closed. |
||
0 ignored issues
–
show
|
|||
62 | * It is kept here for a quick access to the close command. |
||
63 | */ |
||
64 | private MultistageCloseCommand closeCommand; |
||
65 | |||
66 | /** |
||
67 | * The command that closes an active conversation with the bot, indicating to the active command that the conversation is done. |
||
0 ignored issues
–
show
|
|||
68 | * It is kept here for a quick access to the done command. |
||
69 | */ |
||
70 | private MultistageDoneCommand doneCommand; |
||
71 | |||
72 | /** |
||
73 | * The command that shows the help of the bot. |
||
74 | * It is kept here for a quick access to the help command. |
||
75 | */ |
||
76 | private HelpCommand helpCommand; |
||
77 | |||
78 | /** |
||
79 | * Constructs a new {@link CommandManager} instance with the list of available {@link Command} |
||
80 | * and the properties of the commands. |
||
81 | * |
||
82 | * @param commands the list of available {@link Command}. |
||
83 | * @param commandProperties the properties of the commands. |
||
84 | */ |
||
85 | @Autowired |
||
86 | public CommandManager(List<Command> commands, CommandProperties commandProperties) { |
||
0 ignored issues
–
show
|
|||
87 | this.commandsByFullName = new HashMap<>(); |
||
0 ignored issues
–
show
|
|||
88 | this.commands = new LinkedList<>(); |
||
89 | this.nonSecureCommands = new LinkedList<>(); |
||
90 | |||
91 | this.activeCommand = new HashMap<>(); |
||
92 | this.commandProperties = commandProperties; |
||
93 | |||
94 | commands.stream() |
||
95 | .filter(item -> item.getClass().isAnnotationPresent(TelegramCommand.class)) |
||
0 ignored issues
–
show
|
|||
96 | .forEach(this::addCommand); |
||
97 | } |
||
98 | |||
99 | /** |
||
100 | * Adds the command to the list of available/non-secure commands and sets the commands for |
||
101 | * {@link #closeCommand}, {@link #doneCommand} and {@link #helpCommand}. |
||
102 | * |
||
103 | * @param command the command to be added. |
||
104 | * @see #commandsByFullName |
||
105 | * @see #commands |
||
106 | * @see #nonSecureCommands |
||
107 | * @see #closeCommand |
||
108 | * @see #doneCommand |
||
109 | * @see #helpCommand |
||
110 | */ |
||
111 | private void addCommand(Command command) { |
||
0 ignored issues
–
show
|
|||
112 | // Registering the command for each name |
||
113 | for (String name : getCommandNames(command)) { |
||
0 ignored issues
–
show
|
|||
114 | |||
115 | if (!isValidCommandName(name)) { |
||
0 ignored issues
–
show
|
|||
116 | LOG.warn(String.format("Command %s has a duplicate or empty name. It will be discarded.", |
||
0 ignored issues
–
show
|
|||
117 | command.getClass().getSimpleName())); |
||
118 | break; |
||
119 | } |
||
120 | |||
121 | this.commandsByFullName.put(getCommandFullName(name), command); |
||
122 | |||
123 | if (!this.commands.contains(command)) { |
||
0 ignored issues
–
show
|
|||
124 | this.commands.add(command); |
||
0 ignored issues
–
show
|
|||
125 | } |
||
126 | |||
127 | // Just for keeping a reference of the non-secure commands |
||
128 | if ((commandProperties.getNonSecure().contains(name) |
||
129 | || !command.getClass().getAnnotation(TelegramCommand.class).secure()) |
||
130 | && !this.nonSecureCommands.contains(command)) { |
||
0 ignored issues
–
show
|
|||
131 | this.nonSecureCommands.add(command); |
||
0 ignored issues
–
show
|
|||
132 | } |
||
133 | |||
134 | if (command instanceof MultistageCloseCommand) { |
||
0 ignored issues
–
show
|
|||
135 | closeCommand = (MultistageCloseCommand) command; |
||
0 ignored issues
–
show
|
|||
136 | } else if (command instanceof MultistageDoneCommand) { |
||
0 ignored issues
–
show
|
|||
137 | doneCommand = (MultistageDoneCommand) command; |
||
0 ignored issues
–
show
|
|||
138 | } else if (command instanceof HelpCommand) { |
||
0 ignored issues
–
show
|
|||
139 | helpCommand = (HelpCommand) command; |
||
0 ignored issues
–
show
|
|||
140 | } |
||
141 | } |
||
142 | } |
||
143 | |||
144 | /** |
||
145 | * Returns {@code true} if the command name is a valid name for a command. |
||
146 | * |
||
147 | * @param commandName the name of the command to validate. |
||
148 | * @return {@code true} if the command name is a valid name for a command; {@code false} otherwise. |
||
149 | */ |
||
150 | private boolean isValidCommandName(String commandName) { |
||
0 ignored issues
–
show
|
|||
151 | return !commandName.trim().isEmpty() && !this.commandsByFullName.containsKey(commandName); |
||
0 ignored issues
–
show
|
|||
152 | } |
||
153 | |||
154 | /** |
||
155 | * Returns {@code true} if the command is non-secure. |
||
156 | * |
||
157 | * @param command the command to be processed. |
||
158 | * @return {@code true} if the command is non-secure; {@code false} otherwise. |
||
159 | */ |
||
160 | public boolean isNonSecureCommand(Command command) { |
||
0 ignored issues
–
show
|
|||
161 | return this.nonSecureCommands.contains(command); |
||
0 ignored issues
–
show
|
|||
162 | } |
||
163 | |||
164 | /** |
||
165 | * Returns the names of the command (the same names declared on {@link TelegramCommand#name()}). |
||
166 | * The names may include the slash (/). |
||
167 | * |
||
168 | * @param command the command from which the names will be identified. |
||
169 | * @return the names of the command. |
||
170 | */ |
||
171 | public static String[] getCommandNames(Command command) { |
||
0 ignored issues
–
show
|
|||
172 | return command.getClass().getAnnotation(TelegramCommand.class).name(); |
||
0 ignored issues
–
show
|
|||
173 | } |
||
174 | |||
175 | /** |
||
176 | * Returns the name of the command. It includes the slash (/). |
||
177 | * |
||
178 | * @param commandName the command's name (the same name declared on {@link TelegramCommand#name()}) |
||
179 | * @return the name of the command. |
||
180 | */ |
||
181 | public static String getCommandFullName(String commandName) { |
||
0 ignored issues
–
show
|
|||
182 | return commandName.startsWith("/") ? commandName : String.format("/%s", commandName); |
||
0 ignored issues
–
show
|
|||
183 | } |
||
184 | |||
185 | /** |
||
186 | * Returns the names of the command. They include the slash (/). |
||
187 | * |
||
188 | * @param command the command from which the names will be identified. |
||
189 | * @return the names of the command. |
||
190 | */ |
||
191 | public static List<String> getCommandFullNames(Command command) { |
||
0 ignored issues
–
show
|
|||
192 | List<String> commandFullNames = new LinkedList<>(); |
||
0 ignored issues
–
show
|
|||
193 | |||
194 | for (String name : getCommandNames(command)) { |
||
0 ignored issues
–
show
|
|||
195 | commandFullNames.add(getCommandFullName(name)); |
||
0 ignored issues
–
show
|
|||
196 | } |
||
197 | |||
198 | return commandFullNames; |
||
199 | } |
||
200 | |||
201 | /** |
||
202 | * Returns the {@link Command} instance from the command name. |
||
203 | * |
||
204 | * @param command the command name. |
||
205 | * @return the {@link Command} instance. |
||
206 | * @throws CommandNotFound if the name is not related to any commands. |
||
207 | */ |
||
208 | public Command getCommand(String command) throws CommandNotFound { |
||
0 ignored issues
–
show
|
|||
209 | if (!this.commandsByFullName.containsKey(command)) { |
||
0 ignored issues
–
show
|
|||
210 | throw new CommandNotFound(); |
||
0 ignored issues
–
show
As defined in your coding style, remove the checked exception:
CommandNotFound . Consider throwing an unchecked exception instead.
Opionions on checked exceptions are divided. On the positive side they do make code safer and provide self-documentation on possible error conditions that the caller has to be aware of. On the negative side, they do force the caller to deal with these errors, either by catching or specifying them in the method definition. This may make the code more verbose than necessary. The Java documentation recommends using them wisely. ![]() |
|||
211 | } |
||
212 | |||
213 | return this.commandsByFullName.get(command); |
||
214 | } |
||
215 | |||
216 | /** |
||
217 | * Sets the multistage command as the active one. |
||
218 | * |
||
219 | * @param idChat the chat identification of the user for whom the multistage command will be active. |
||
220 | * @param command the command to activate. |
||
221 | */ |
||
222 | public void setActiveCommand(final Long idChat, final MultistageCommand command) { |
||
0 ignored issues
–
show
|
|||
223 | this.activeCommand.put(idChat, command); |
||
0 ignored issues
–
show
|
|||
224 | } |
||
225 | |||
226 | /** |
||
227 | * Gets the active command for the user. |
||
228 | * |
||
229 | * @param idChat the chat identification of the user. |
||
230 | * @return the active command. |
||
231 | * @throws CommandNotActive if there is no an active command. |
||
232 | */ |
||
233 | public MultistageCommand getActiveCommand(Long idChat) throws CommandNotActive { |
||
0 ignored issues
–
show
|
|||
234 | if (!hasActiveCommand(idChat)) { |
||
0 ignored issues
–
show
|
|||
235 | throw new CommandNotActive(); |
||
0 ignored issues
–
show
As defined in your coding style, remove the checked exception:
CommandNotActive . Consider throwing an unchecked exception instead.
Opionions on checked exceptions are divided. On the positive side they do make code safer and provide self-documentation on possible error conditions that the caller has to be aware of. On the negative side, they do force the caller to deal with these errors, either by catching or specifying them in the method definition. This may make the code more verbose than necessary. The Java documentation recommends using them wisely. ![]() |
|||
236 | } |
||
237 | |||
238 | return activeCommand.get(idChat); |
||
239 | } |
||
240 | |||
241 | /** |
||
242 | * Removes the active command. |
||
243 | * |
||
244 | * @param idChat the chat identification of the user. |
||
245 | */ |
||
246 | public void removeActiveCommand(Long idChat) { |
||
0 ignored issues
–
show
|
|||
247 | activeCommand.remove(idChat); |
||
0 ignored issues
–
show
|
|||
248 | } |
||
249 | |||
250 | /** |
||
251 | * Returns {@code true} if there is an active command. |
||
252 | * |
||
253 | * @param idChat the chat identification of the user. |
||
254 | * @return {@code true} if there is an active command; {@code false} otherwise. |
||
255 | */ |
||
256 | public boolean hasActiveCommand(Long idChat) { |
||
0 ignored issues
–
show
|
|||
257 | return activeCommand.containsKey(idChat); |
||
0 ignored issues
–
show
|
|||
258 | } |
||
259 | |||
260 | /** |
||
261 | * Returns the {@link MultistageCloseCommand} if it exists. |
||
262 | * |
||
263 | * @return the {@link MultistageCloseCommand} instance. |
||
264 | */ |
||
265 | public Optional<MultistageCloseCommand> getCloseCommand() { |
||
0 ignored issues
–
show
|
|||
266 | return Optional.ofNullable(this.closeCommand); |
||
0 ignored issues
–
show
|
|||
267 | } |
||
268 | |||
269 | /** |
||
270 | * Returns the {@link MultistageDoneCommand} if it exists. |
||
271 | * |
||
272 | * @return the {@link MultistageDoneCommand} instance. |
||
273 | */ |
||
274 | public Optional<MultistageDoneCommand> getDoneCommand() { |
||
0 ignored issues
–
show
|
|||
275 | return Optional.ofNullable(this.doneCommand); |
||
0 ignored issues
–
show
|
|||
276 | } |
||
277 | |||
278 | /** |
||
279 | * Returns the {@link HelpCommand} if it exists. |
||
280 | * |
||
281 | * @return the {@link HelpCommand} instance. |
||
282 | */ |
||
283 | public Optional<HelpCommand> getHelpCommand() { |
||
0 ignored issues
–
show
|
|||
284 | return Optional.ofNullable(this.helpCommand); |
||
0 ignored issues
–
show
|
|||
285 | } |
||
286 | |||
287 | /** |
||
288 | * Returns a list with the available commands. |
||
289 | * |
||
290 | * @return the available commands. |
||
291 | */ |
||
292 | public List<Command> getAvailableCommands() { |
||
0 ignored issues
–
show
|
|||
293 | return commands; |
||
0 ignored issues
–
show
|
|||
294 | } |
||
295 | |||
296 | /** |
||
297 | * Returns a list with the available non-secure commands. |
||
298 | * |
||
299 | * @return the available non-secure commands. |
||
300 | */ |
||
301 | public List<Command> getAvailableNonSecureCommands() { |
||
0 ignored issues
–
show
|
|||
302 | return nonSecureCommands; |
||
0 ignored issues
–
show
|
|||
303 | } |
||
304 | } |
||
305 |