Passed
Push — master ( 563beb...b73ca5 )
by Roannel Fernández
05:09
created

getAvailableCommands(Long)   A

Complexity

Conditions 2

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 4
dl 0
loc 6
rs 10
c 0
b 0
f 0
cc 2
1
package com.github.netkorp.telegram.framework.commands.basic;
0 ignored issues
show
Code Smell introduced by
It is a best practice to supply a copyright/licence header in your code. Your organisation probably has a template for that.
Loading history...
2
3
import com.github.netkorp.telegram.framework.annotations.TelegramCommand;
4
import com.github.netkorp.telegram.framework.commands.abstracts.AbstractSimpleCommand;
5
import com.github.netkorp.telegram.framework.commands.interfaces.Command;
6
import com.github.netkorp.telegram.framework.commands.interfaces.HelpCommand;
7
import com.github.netkorp.telegram.framework.condition.ExcludeCondition;
8
import com.github.netkorp.telegram.framework.exceptions.CommandNotFound;
9
import com.github.netkorp.telegram.framework.managers.CommandManager;
10
import com.github.netkorp.telegram.framework.managers.SecurityManager;
11
import org.apache.logging.log4j.util.Strings;
12
import org.springframework.beans.factory.annotation.Autowired;
13
import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate;
14
import org.springframework.context.NoSuchMessageException;
15
import org.springframework.context.annotation.Conditional;
16
import org.springframework.context.i18n.LocaleContextHolder;
17
import org.telegram.telegrambots.meta.api.objects.Update;
18
19
import java.util.Collection;
20
import java.util.LinkedList;
21
import java.util.List;
22
import java.util.SortedMap;
23
import java.util.StringJoiner;
24
import java.util.TreeMap;
25
26
/**
27
 * Displays the bot's help.
28
 */
29
@TelegramCommand(name = "help", group = "Basic")
30
@Conditional(ExcludeCondition.class)
31
@ConditionalOnSingleCandidate(HelpCommand.class)
32
public class BasicHelpCommand extends AbstractSimpleCommand implements HelpCommand {
0 ignored issues
show
Coding Style introduced by
Move this left curly brace to the beginning of next line of code.
Loading history...
33
34
    /**
35
     * The component to know which user is authorized.
36
     */
37
    private final SecurityManager securityManager;
0 ignored issues
show
Coding Style introduced by
Make this line start at column 3.
Loading history...
38
39
    /**
40
     * Constructs a new {@link BasicHelpCommand} instance with the {@link SecurityManager} component instance.
41
     *
42
     * @param securityManager the {@link SecurityManager} component instance.
43
     */
44
    @Autowired
45
    public BasicHelpCommand(SecurityManager securityManager) {
0 ignored issues
show
Coding Style introduced by
Move this left curly brace to the beginning of next line of code.
Loading history...
46
        this.securityManager = securityManager;
0 ignored issues
show
Coding Style introduced by
Make this line start at column 5.
Loading history...
47
    }
48
49
    /**
50
     * Processes the data sent by the users.
51
     *
52
     * @param update the received message.
53
     * @param args   the parameters passed to the command execution.
54
     */
55
    @Override
56
    public void execute(final Update update, String[] args) {
0 ignored issues
show
Coding Style introduced by
Move this left curly brace to the beginning of next line of code.
Loading history...
57
        try {
0 ignored issues
show
Coding Style introduced by
Make this line start at column 5.
Loading history...
Coding Style introduced by
Move this left curly brace to the beginning of next line of code.
Loading history...
58
            if (args.length == 0) {
0 ignored issues
show
Coding Style introduced by
Make this line start at column 7.
Loading history...
Coding Style introduced by
Move this left curly brace to the beginning of next line of code.
Loading history...
59
                execute(update);
0 ignored issues
show
Coding Style introduced by
Make this line start at column 9.
Loading history...
60
                return;
61
            }
62
63
            StringJoiner stringJoiner = new StringJoiner(System.lineSeparator());
64
65
            for (String arg : args) {
0 ignored issues
show
Coding Style introduced by
Move this left curly brace to the beginning of next line of code.
Loading history...
66
                try {
0 ignored issues
show
Coding Style introduced by
Make this line start at column 9.
Loading history...
Coding Style introduced by
Move this left curly brace to the beginning of next line of code.
Loading history...
Comprehensibility introduced by
Extract this nested try block into a separate method.
Loading history...
67
                    stringJoiner.add(helpForCommand(commandManager.getCommand(CommandManager.getCommandFullName(arg))));
0 ignored issues
show
Coding Style introduced by
Make this line start at column 11.
Loading history...
68
                } catch (CommandNotFound commandNotFound) {
0 ignored issues
show
Coding Style introduced by
Move this left curly brace to the beginning of next line of code.
Loading history...
Coding Style introduced by
Move this "catch" keyword to a new dedicated line.
Loading history...
69
                    bot.sendMessage(String.format("%s: %s", commandNotFound.getMessage(), arg), update.getMessage().getChatId(), true);
0 ignored issues
show
Coding Style introduced by
Make this line start at column 11.
Loading history...
Coding Style introduced by
This line is 135 characters long, which is over the set limit of 120 characters.
Loading history...
70
                    execute(update);
71
                    throw commandNotFound;
0 ignored issues
show
Best Practice introduced by
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.

Loading history...
72
                }
73
            }
74
75
            bot.sendMessage(stringJoiner.toString(), update.getMessage().getChatId(), true);
76
        } catch (CommandNotFound commandNotFound) {
0 ignored issues
show
Coding Style introduced by
Move this left curly brace to the beginning of next line of code.
Loading history...
Coding Style introduced by
Move this "catch" keyword to a new dedicated line.
Loading history...
Best Practice introduced by
Ignoring an Exception may lead to hard to find bugs. Consider logging or rethrowing the original exception. If you want to throw a different exception, you can set the original exception as its cause to preserve the stacktrace.

When instantiating a new Exception, you can set another Exception as its cause.

See the Oracle documentation on Throwables.

Usage example

throw new Exception("Exception Message", originalException);

Complete Example:

class ReThrowException {
  public static void throwsException() {
        try {
            throw new Exception("I am the original exception");
        } catch (final Exception e) {
            throw new RuntimeException("I am the new exception", e);
        }
    }
    public static void main(String[] args) {
        try {
            throwsException();
        }
        catch (final RuntimeException e) {
            System.out.println(e.getMessage());
            System.out.println("and my cause is: " + e.getCause().getMessage());
            e.printStackTrace();
        }
    }
}
Loading history...
77
            // Do nothing
78
        }
79
    }
80
81
    /**
82
     * Executes the command's logic without taking parameters.
83
     *
84
     * @param update the received message.
85
     */
86
    @Override
87
    public void execute(Update update) {
0 ignored issues
show
Coding Style introduced by
Move this left curly brace to the beginning of next line of code.
Loading history...
88
        StringJoiner stringJoiner = new StringJoiner(System.lineSeparator());
0 ignored issues
show
Coding Style introduced by
Make this line start at column 5.
Loading history...
89
90
        stringJoiner.add(String.format("%s:", messageSource.getMessage("commands.basic.help.title", null,
91
                LocaleContextHolder.getLocale())));
92
93
        commandsByGroup(getAvailableCommands(update.getMessage().getChatId()))
94
                .forEach((group, commands) -> stringJoiner.add(helpForGroup(group, commands)));
0 ignored issues
show
introduced by
Specify a type for: 'group', 'commands'
Loading history...
95
96
        bot.sendMessage(stringJoiner.toString(), update.getMessage().getChatId(), true);
97
    }
98
99
    /**
100
     * Returns the help for the group of commands.
101
     *
102
     * @param group    the name of the group.
103
     * @param commands the commands into the group.
104
     * @return the help for the group.
105
     */
106
    private String helpForGroup(String group, List<Command> commands) {
0 ignored issues
show
Coding Style introduced by
Move this left curly brace to the beginning of next line of code.
Loading history...
107
        StringJoiner stringJoiner = new StringJoiner(System.lineSeparator());
0 ignored issues
show
Coding Style introduced by
Make this line start at column 5.
Loading history...
108
        if (!Strings.isEmpty(group)) {
0 ignored issues
show
Coding Style introduced by
Move this left curly brace to the beginning of next line of code.
Loading history...
109
            stringJoiner.add(String.format("<b>%s</b>", group));
0 ignored issues
show
Coding Style introduced by
Make this line start at column 7.
Loading history...
110
        }
111
        commands.forEach(command -> stringJoiner.add(helpForCommand(command)));
0 ignored issues
show
introduced by
Specify a type for: 'command'
Loading history...
112
        return System.lineSeparator() + stringJoiner.toString();
113
    }
114
115
    /**
116
     * Returns the help for a single command.
117
     *
118
     * @param command the command.
119
     * @return the help of the command.
120
     */
121
    private String helpForCommand(Command command) {
0 ignored issues
show
Coding Style introduced by
Move this left curly brace to the beginning of next line of code.
Loading history...
122
        StringJoiner stringJoiner = new StringJoiner(", ");
0 ignored issues
show
Coding Style introduced by
Make this line start at column 5.
Loading history...
123
        CommandManager.getCommandFullNames(command).forEach(stringJoiner::add);
124
125
        String description;
126
127
        try {
0 ignored issues
show
Coding Style introduced by
Move this left curly brace to the beginning of next line of code.
Loading history...
128
            description = messageSource.getMessage(command.descriptionKey(), null, LocaleContextHolder.getLocale());
0 ignored issues
show
Coding Style introduced by
Make this line start at column 7.
Loading history...
129
        } catch (NoSuchMessageException exception) {
0 ignored issues
show
Coding Style introduced by
Move this left curly brace to the beginning of next line of code.
Loading history...
Coding Style introduced by
Move this "catch" keyword to a new dedicated line.
Loading history...
Best Practice introduced by
Ignoring an Exception may lead to hard to find bugs. Consider logging or rethrowing the original exception. If you want to throw a different exception, you can set the original exception as its cause to preserve the stacktrace.

When instantiating a new Exception, you can set another Exception as its cause.

See the Oracle documentation on Throwables.

Usage example

throw new Exception("Exception Message", originalException);

Complete Example:

class ReThrowException {
  public static void throwsException() {
        try {
            throw new Exception("I am the original exception");
        } catch (final Exception e) {
            throw new RuntimeException("I am the new exception", e);
        }
    }
    public static void main(String[] args) {
        try {
            throwsException();
        }
        catch (final RuntimeException e) {
            System.out.println(e.getMessage());
            System.out.println("and my cause is: " + e.getCause().getMessage());
            e.printStackTrace();
        }
    }
}
Loading history...
130
            description = messageSource.getMessage("commands.basic.help.default-description", null,
0 ignored issues
show
Coding Style introduced by
Make this line start at column 7.
Loading history...
131
                    LocaleContextHolder.getLocale());
132
        }
133
134
        return String.format("%s - %s", stringJoiner.toString(), description);
135
    }
136
137
    /**
138
     * Organizes the available commands into groups.
139
     *
140
     * @param commands the list with all the available commands.
141
     * @return the available commands in groups sorted by the group's name.
142
     */
143
    private SortedMap<String, List<Command>> commandsByGroup(Collection<Command> commands) {
0 ignored issues
show
Coding Style introduced by
Move this left curly brace to the beginning of next line of code.
Loading history...
Comprehensibility introduced by
Private methods that do not access instance data should be made static. This makes the code more readable and may enable the compiler to optimize your code. Consider making commandsByGroupstatic.
Loading history...
144
        SortedMap<String, List<Command>> commandsByGroup = new TreeMap<>();
0 ignored issues
show
Coding Style introduced by
Make this line start at column 5.
Loading history...
145
146
        commands.forEach(command -> {
0 ignored issues
show
introduced by
Specify a type for: 'command'
Loading history...
147
            // Groups
148
            String group = command.getClass().getAnnotation(TelegramCommand.class).group();
0 ignored issues
show
Coding Style introduced by
Make this line start at column 11.
Loading history...
149
150
            List<Command> commandList = commandsByGroup.getOrDefault(group, new LinkedList<>());
151
            commandList.add(command);
152
153
            commandsByGroup.put(group, commandList);
154
        });
155
156
        return commandsByGroup;
157
    }
158
159
    /**
160
     * Returns the list of available commands for the user. It takes into account whether the user is authorized or not.
161
     *
162
     * @param chatId the identification of the user's chat.
163
     * @return the list of available commands.
164
     */
165
    private Collection<Command> getAvailableCommands(Long chatId) {
0 ignored issues
show
Coding Style introduced by
Move this left curly brace to the beginning of next line of code.
Loading history...
166
        if (securityManager.isAuthorized(chatId)) {
0 ignored issues
show
Coding Style introduced by
Make this line start at column 5.
Loading history...
Coding Style introduced by
Move this left curly brace to the beginning of next line of code.
Loading history...
167
            return commandManager.getAvailableCommands();
0 ignored issues
show
Coding Style introduced by
Make this line start at column 7.
Loading history...
168
        }
169
170
        return commandManager.getAvailableNonSecureCommands();
171
    }
172
173
    /**
174
     * Returns the command's description key, used to retrieve the help message.
175
     *
176
     * @return the command's description key.
177
     */
178
    @Override
179
    public String descriptionKey() {
0 ignored issues
show
Coding Style introduced by
Move this left curly brace to the beginning of next line of code.
Loading history...
180
        return "commands.description.help";
0 ignored issues
show
Coding Style introduced by
Make this line start at column 5.
Loading history...
181
    }
182
}
183