001/*
002 * Copyright (c) 2016-2018 Daniel Ennis (Aikar) - MIT License
003 *
004 *  Permission is hereby granted, free of charge, to any person obtaining
005 *  a copy of this software and associated documentation files (the
006 *  "Software"), to deal in the Software without restriction, including
007 *  without limitation the rights to use, copy, modify, merge, publish,
008 *  distribute, sublicense, and/or sell copies of the Software, and to
009 *  permit persons to whom the Software is furnished to do so, subject to
010 *  the following conditions:
011 *
012 *  The above copyright notice and this permission notice shall be
013 *  included in all copies or substantial portions of the Software.
014 *
015 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
016 *  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
017 *  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
018 *  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
019 *  LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
020 *  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
021 *  WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
022 */
023
024package co.aikar.commands;
025
026import co.aikar.commands.annotation.Conditions;
027import co.aikar.commands.annotation.Default;
028import co.aikar.commands.annotation.Description;
029import co.aikar.commands.annotation.Flags;
030import co.aikar.commands.annotation.Optional;
031import co.aikar.commands.annotation.Syntax;
032import co.aikar.commands.annotation.Values;
033import co.aikar.commands.contexts.ContextResolver;
034import co.aikar.commands.contexts.IssuerAwareContextResolver;
035import co.aikar.commands.contexts.IssuerOnlyContextResolver;
036import co.aikar.commands.contexts.OptionalContextResolver;
037import com.google.common.collect.Maps;
038
039import java.lang.reflect.Parameter;
040import java.util.Map;
041
042public class CommandParameter <CEC extends CommandExecutionContext<CEC, ? extends CommandIssuer>> {
043    private final Parameter parameter;
044    private final Class<?> type;
045    private final String name;
046    private final CommandManager manager;
047    private final int paramIndex;
048
049    private ContextResolver<?, CEC> resolver;
050    private boolean optional;
051    private String description;
052    private String defaultValue;
053    private String syntax;
054    private String conditions;
055    private boolean requiresInput;
056    private boolean commandIssuer;
057    private String[] values;
058    private Map<String, String> flags;
059    private boolean canConsumeInput;
060    private boolean optionalResolver;
061
062    public CommandParameter(RegisteredCommand<CEC> command, Parameter param, int paramIndex) {
063        this.parameter = param;
064        this.type = param.getType();
065        this.name = param.getName(); // do we care for an annotation to supply name?
066        //noinspection unchecked
067        this.manager = command.manager;
068        this.paramIndex = paramIndex;
069        Annotations annotations = manager.getAnnotations();
070
071        this.defaultValue = annotations.getAnnotationValue(param, Default.class, Annotations.REPLACEMENTS | (type != String.class ? Annotations.NO_EMPTY : 0));
072        this.description = annotations.getAnnotationValue(param, Description.class, Annotations.REPLACEMENTS | Annotations.DEFAULT_EMPTY);
073        this.conditions = annotations.getAnnotationValue(param, Conditions.class, Annotations.REPLACEMENTS | Annotations.NO_EMPTY);
074
075        //noinspection unchecked
076        this.resolver = manager.getCommandContexts().getResolver(type);
077        if (this.resolver == null) {
078            ACFUtil.sneaky(new InvalidCommandContextException(
079                    "Parameter " + type.getSimpleName() + " of " + command + " has no applicable context resolver"
080            ));
081        }
082
083        this.optional = annotations.hasAnnotation(param, Optional.class) || this.defaultValue != null;
084        this.optionalResolver = isOptionalResolver(resolver);
085        this.requiresInput = !this.optional && !this.optionalResolver;
086        //noinspection unchecked
087        this.commandIssuer = paramIndex == 0 && manager.isCommandIssuer(type);
088        this.canConsumeInput = !this.commandIssuer && !(resolver instanceof IssuerOnlyContextResolver);
089
090        this.values = annotations.getAnnotationValues(param, Values.class, Annotations.REPLACEMENTS | Annotations.NO_EMPTY);
091
092        this.syntax = null;
093        if (!commandIssuer) {
094            this.syntax = annotations.getAnnotationValue(param, Syntax.class);
095            if (syntax == null) {
096                if (!requiresInput && canConsumeInput) {
097                    this.syntax = "[" + name + "]";
098                } else if (requiresInput) {
099                    this.syntax = "<" + name + ">";
100                }
101            }
102        }
103
104        this.flags = Maps.newHashMap();
105        String flags = annotations.getAnnotationValue(param, Flags.class, Annotations.REPLACEMENTS | Annotations.NO_EMPTY);
106        if (flags != null) {
107            parseFlags(flags);
108        }
109        inheritContextFlags(command.scope);
110    }
111
112    private void inheritContextFlags(BaseCommand scope) {
113        if (!scope.contextFlags.isEmpty()) {
114            Class<?> pCls = this.type;
115            do {
116                parseFlags(scope.contextFlags.get(pCls));
117            } while ((pCls = pCls.getSuperclass()) != null);
118        }
119        if (scope.parentCommand != null) {
120            inheritContextFlags(scope.parentCommand);
121        }
122    }
123
124    private void parseFlags(String flags) {
125        if (flags != null) {
126            for (String s : ACFPatterns.COMMA.split(manager.getCommandReplacements().replace(flags))) {
127                String[] v = ACFPatterns.EQUALS.split(s, 2);
128                if (!this.flags.containsKey(v[0])) {
129                    this.flags.put(v[0], v.length > 1 ? v[1] : null);
130                }
131            }
132        }
133    }
134
135    private boolean isOptionalResolver(ContextResolver<?, CEC> resolver) {
136        return resolver instanceof IssuerAwareContextResolver
137            || resolver instanceof IssuerOnlyContextResolver
138            || resolver instanceof OptionalContextResolver;
139    }
140
141
142    public Parameter getParameter() {
143        return parameter;
144    }
145
146    public Class<?> getType() {
147        return type;
148    }
149
150    public String getName() {
151        return name;
152    }
153
154    public CommandManager getManager() {
155        return manager;
156    }
157
158    public int getParamIndex() {
159        return paramIndex;
160    }
161
162    public ContextResolver<?, CEC> getResolver() {
163        return resolver;
164    }
165
166    public void setResolver(ContextResolver<?, CEC> resolver) {
167        this.resolver = resolver;
168    }
169
170    public boolean isOptional() {
171        return optional;
172    }
173
174    public void setOptional(boolean optional) {
175        this.optional = optional;
176    }
177
178    public String getDescription() {
179        return description;
180    }
181
182    public void setDescription(String description) {
183        this.description = description;
184    }
185
186    public String getDefaultValue() {
187        return defaultValue;
188    }
189
190    public void setDefaultValue(String defaultValue) {
191        this.defaultValue = defaultValue;
192    }
193
194    public boolean isCommandIssuer() {
195        return commandIssuer;
196    }
197
198    public void setCommandIssuer(boolean commandIssuer) {
199        this.commandIssuer = commandIssuer;
200    }
201
202    public String[] getValues() {
203        return values;
204    }
205
206    public void setValues(String[] values) {
207        this.values = values;
208    }
209
210    public Map<String, String> getFlags() {
211        return flags;
212    }
213
214    public void setFlags(Map<String, String> flags) {
215        this.flags = flags;
216    }
217
218    public boolean canConsumeInput() {
219        return canConsumeInput;
220    }
221
222    public void setCanConsumeInput(boolean canConsumeInput) {
223        this.canConsumeInput = canConsumeInput;
224    }
225
226    public void setOptionalResolver(boolean optionalResolver) {
227        this.optionalResolver = optionalResolver;
228    }
229
230    public boolean isOptionalResolver() {
231        return optionalResolver;
232    }
233
234    public boolean requiresInput() {
235        return requiresInput;
236    }
237
238    public void setRequiresInput(boolean requiresInput) {
239        this.requiresInput = requiresInput;
240    }
241
242    public String getSyntax() {
243        return syntax;
244    }
245
246    public void setSyntax(String syntax) {
247        this.syntax = syntax;
248    }
249
250    public String getConditions() {
251        return conditions;
252    }
253
254    public void setConditions(String conditions) {
255        this.conditions = conditions;
256    }
257}