Passed
Pull Request — master (#172)
by Vincent
13:00
created

supports(Command,A)   A

Complexity

Conditions 1

Size

Total Lines 3
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 3
dl 0
loc 3
ccs 1
cts 1
cp 1
crap 1
rs 10
c 1
b 0
f 0
1
/*
2
 * This file is part of Araknemu.
3
 *
4
 * Araknemu is free software: you can redistribute it and/or modify
5
 * it under the terms of the GNU Lesser General Public License as published by
6
 * the Free Software Foundation, either version 3 of the License, or
7
 * (at your option) any later version.
8
 *
9
 * Araknemu is distributed in the hope that it will be useful,
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
 * GNU Lesser General Public License for more details.
13
 *
14
 * You should have received a copy of the GNU Lesser General Public License
15
 * along with Araknemu.  If not, see <https://www.gnu.org/licenses/>.
16
 *
17
 * Copyright (c) 2017-2021 Vincent Quatrevieux
18
 */
19
20
package fr.quatrevieux.araknemu.game.admin.executor.argument;
21
22
import fr.quatrevieux.araknemu.game.admin.Command;
23
import fr.quatrevieux.araknemu.game.admin.CommandParser;
24
import fr.quatrevieux.araknemu.game.admin.executor.argument.handler.DurationOptionHandler;
25
import fr.quatrevieux.araknemu.game.admin.executor.argument.handler.IpAddressStringHandler;
26
import fr.quatrevieux.araknemu.game.admin.executor.argument.handler.LocalTimeHandler;
27
import fr.quatrevieux.araknemu.game.admin.executor.argument.type.SubArguments;
28
import fr.quatrevieux.araknemu.game.admin.help.CommandHelp;
29
import inet.ipaddr.IPAddressString;
30
import org.kohsuke.args4j.CmdLineParser;
31
import org.kohsuke.args4j.OptionHandlerRegistry;
32
import org.kohsuke.args4j.ParserProperties;
33
import org.kohsuke.args4j.spi.OptionHandler;
34
import org.kohsuke.args4j.spi.SubCommandHandler;
35
36
import java.io.ByteArrayOutputStream;
37
import java.time.Duration;
38
import java.time.LocalTime;
39
import java.util.HashMap;
40
import java.util.List;
41
import java.util.Map;
42
import java.util.function.Function;
43
44
/**
45
 * Hydrator using {@link org.kohsuke.args4j.CmdLineParser} parser for fill arguments
46
 * This is the default hydrator
47
 */
48
public final class AnnotationHydrator implements ArgumentsHydrator {
49 1
    private final ParserProperties parserProperties = ParserProperties.defaults().withAtSyntax(false);
50
51 1
    public AnnotationHydrator() {
52 1
        OptionHandlerRegistry.getRegistry().registerHandler(Duration.class, DurationOptionHandler::new);
53 1
        OptionHandlerRegistry.getRegistry().registerHandler(IPAddressString.class, IpAddressStringHandler::new);
54 1
        OptionHandlerRegistry.getRegistry().registerHandler(LocalTime.class, LocalTimeHandler::new);
55 1
        OptionHandlerRegistry.getRegistry().registerHandler(SubArguments.class, SubCommandHandler::new);
56 1
    }
57
58
    @Override
59
    public <A> A hydrate(Command<A> command, A commandArguments, CommandParser.Arguments parsedArguments) throws Exception {
60 1
        final CmdLineParser parser = new CmdLineParser(commandArguments, parserProperties);
61
62
        // @todo handle custom properties
0 ignored issues
show
introduced by
Comment matches to-do format '(TODO:)|(@todo )'.
Loading history...
63 1
        parser.parseArgument(parsedArguments.arguments().subList(1, parsedArguments.arguments().size()));
64
65 1
        return commandArguments;
66
    }
67
68
    @Override
69
    public <A> CommandHelp help(Command<A> command, A commandArguments, CommandHelp help) {
70 1
        final HelpGenerator generator = new HelpGenerator(command, new CmdLineParser(commandArguments, parserProperties));
71
72 1
        return help.modify(builder -> {
73 1
            generator.arguments(builder);
74 1
            generator.options(builder);
75 1
            generator.synopsis(builder);
76 1
        });
77
    }
78
79
    @Override
80
    public <A> boolean supports(Command<A> command, A commandArguments) {
81 1
        return commandArguments != null;
82
    }
83
84
    private static class HelpGenerator {
85
        private final Command command;
86
        private final CmdLineParser parser;
87 1
        private final Map<String, String> defaultOptionValues = new HashMap<>();
88
89 1
        public HelpGenerator(Command command, CmdLineParser parser) {
90 1
            this.command = command;
91 1
            this.parser = parser;
92 1
        }
93
94
        public void synopsis(CommandHelp.Builder help) {
95 1
            if (!help.hasSynopsis()) {
96 1
                help.synopsis(parseSynopsis());
97
            }
98 1
        }
99
100
        public void options(CommandHelp.Builder help) {
101 1
            parseOptions(parser.getOptions(), option -> option.option.toString(), help);
102 1
        }
103
104
        public void arguments(CommandHelp.Builder help) {
105 1
            parseOptions(parser.getArguments(), argument -> argument.getNameAndMeta(null), help);
106 1
        }
107
108
        private void parseOptions(List<OptionHandler> options, Function<OptionHandler, String> nameMapper, CommandHelp.Builder help) {
109 1
            for (OptionHandler option : options) {
110 1
                final String name = nameMapper.apply(option);
111
112 1
                if (!help.hasOption(name) && !option.option.usage().isEmpty()) {
113 1
                    help.option(name, option.option.usage());
114
                }
115
116 1
                if (!option.option.required()) {
117 1
                    final String defaultValue = option.printDefaultValue();
118
119 1
                    if (!isEmptyDefault(defaultValue)) {
120 1
                        defaultOptionValues.put(name, defaultValue);
121
                    }
122
                }
123 1
            }
124 1
        }
125
126
        private boolean isEmptyDefault(String defaultValue) {
127 1
            return defaultValue == null || defaultValue.isEmpty()
128 1
                || "false".equals(defaultValue) || "0".equals(defaultValue)
129
            ;
130
        }
131
132
        private String parseSynopsis() {
133 1
            final ByteArrayOutputStream os = new ByteArrayOutputStream();
134 1
            parser.printSingleLineUsage(os);
135
136 1
            String synopsis = command.name() + os;
137
138 1
            for (Map.Entry<String, String> mapping : defaultOptionValues.entrySet()) {
139 1
                synopsis = synopsis.replaceFirst("\\[(" + mapping.getKey() + ".*?)\\]", "[$1=" + mapping.getValue() + "]");
140 1
            }
141
142 1
            return synopsis;
143
        }
144
    }
145
}
146