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.CommandPermission;
027import co.aikar.commands.annotation.Conditions;
028import co.aikar.commands.annotation.Default;
029import co.aikar.commands.annotation.Description;
030import co.aikar.commands.annotation.Flags;
031import co.aikar.commands.annotation.Optional;
032import co.aikar.commands.annotation.Single;
033import co.aikar.commands.annotation.Syntax;
034import co.aikar.commands.annotation.Values;
035import co.aikar.commands.contexts.ContextResolver;
036import co.aikar.commands.contexts.IssuerAwareContextResolver;
037import co.aikar.commands.contexts.IssuerOnlyContextResolver;
038import co.aikar.commands.contexts.OptionalContextResolver;
039
040import java.lang.reflect.Parameter;
041import java.util.Arrays;
042import java.util.HashMap;
043import java.util.HashSet;
044import java.util.Map;
045import java.util.Set;
046
047public class CommandParameter<CEC extends CommandExecutionContext<CEC, ? extends CommandIssuer>> {
048    private final Parameter parameter;
049    private final Class<?> type;
050    private final String name;
051    private final CommandManager manager;
052    private final int paramIndex;
053
054    private ContextResolver<?, CEC> resolver;
055    private boolean optional;
056    private Set<String> permissions = new HashSet<>();
057    private String permission;
058    private String description;
059    private String defaultValue;
060    private String syntax;
061    private String conditions;
062    private boolean requiresInput;
063    private boolean commandIssuer;
064    private String[] values;
065    private Map<String, String> flags;
066    private boolean canConsumeInput;
067    private boolean optionalResolver;
068    boolean consumesRest;
069
070    public CommandParameter(RegisteredCommand<CEC> command, Parameter param, int paramIndex, boolean isLast) {
071        this.parameter = param;
072        this.type = param.getType();
073        this.name = param.getName(); // do we care for an annotation to supply name?
074        this.manager = command.manager;
075        this.paramIndex = paramIndex;
076        Annotations annotations = manager.getAnnotations();
077
078        this.defaultValue = annotations.getAnnotationValue(param, Default.class, Annotations.REPLACEMENTS | (type != String.class ? Annotations.NO_EMPTY : 0));
079        this.description = annotations.getAnnotationValue(param, Description.class, Annotations.REPLACEMENTS | Annotations.DEFAULT_EMPTY);
080        this.conditions = annotations.getAnnotationValue(param, Conditions.class, Annotations.REPLACEMENTS | Annotations.NO_EMPTY);
081
082        //noinspection unchecked
083        this.resolver = manager.getCommandContexts().getResolver(type);
084        if (this.resolver == null) {
085            ACFUtil.sneaky(new InvalidCommandContextException(
086                    "Parameter " + type.getSimpleName() + " of " + command + " has no applicable context resolver"
087            ));
088        }
089
090        this.optional = annotations.hasAnnotation(param, Optional.class) || this.defaultValue != null || (isLast && type == String[].class);
091        this.permission = annotations.getAnnotationValue(param, CommandPermission.class, Annotations.REPLACEMENTS | Annotations.NO_EMPTY);
092        this.optionalResolver = isOptionalResolver(resolver);
093        this.requiresInput = !this.optional && !this.optionalResolver;
094        //noinspection unchecked
095        this.commandIssuer = paramIndex == 0 && manager.isCommandIssuer(type);
096        this.canConsumeInput = !this.commandIssuer && !(resolver instanceof IssuerOnlyContextResolver);
097        this.consumesRest = (type == String.class && !annotations.hasAnnotation(param, Single.class)) || (isLast && type == String[].class);
098
099        this.values = annotations.getAnnotationValues(param, Values.class, Annotations.REPLACEMENTS | Annotations.NO_EMPTY);
100
101        this.syntax = null;
102        if (!commandIssuer) {
103            this.syntax = annotations.getAnnotationValue(param, Syntax.class);
104            if (syntax == null) {
105                if (!requiresInput && canConsumeInput) {
106                    this.syntax = "[" + name + "]";
107                } else if (requiresInput) {
108                    this.syntax = "<" + name + ">";
109                }
110            }
111        }
112
113        this.flags = new HashMap<>();
114        String flags = annotations.getAnnotationValue(param, Flags.class, Annotations.REPLACEMENTS | Annotations.NO_EMPTY);
115        if (flags != null) {
116            parseFlags(flags);
117        }
118        inheritContextFlags(command.scope);
119        this.computePermissions();
120    }
121
122    private void inheritContextFlags(BaseCommand scope) {
123        if (!scope.contextFlags.isEmpty()) {
124            Class<?> pCls = this.type;
125            do {
126                parseFlags(scope.contextFlags.get(pCls));
127            } while ((pCls = pCls.getSuperclass()) != null);
128        }
129        if (scope.parentCommand != null) {
130            inheritContextFlags(scope.parentCommand);
131        }
132    }
133
134    private void parseFlags(String flags) {
135        if (flags != null) {
136            for (String s : ACFPatterns.COMMA.split(manager.getCommandReplacements().replace(flags))) {
137                String[] v = ACFPatterns.EQUALS.split(s, 2);
138                if (!this.flags.containsKey(v[0])) {
139                    this.flags.put(v[0], v.length > 1 ? v[1] : null);
140                }
141            }
142        }
143    }
144
145    private void computePermissions() {
146        this.permissions.clear();
147        if (this.permission != null && !this.permission.isEmpty()) {
148            this.permissions.addAll(Arrays.asList(ACFPatterns.COMMA.split(this.permission)));
149        }
150    }
151
152    private boolean isOptionalResolver(ContextResolver<?, CEC> resolver) {
153        return resolver instanceof IssuerAwareContextResolver
154                || resolver instanceof IssuerOnlyContextResolver
155                || resolver instanceof OptionalContextResolver;
156    }
157
158
159    public Parameter getParameter() {
160        return parameter;
161    }
162
163    public Class<?> getType() {
164        return type;
165    }
166
167    public String getName() {
168        return name;
169    }
170
171    public CommandManager getManager() {
172        return manager;
173    }
174
175    public int getParamIndex() {
176        return paramIndex;
177    }
178
179    public ContextResolver<?, CEC> getResolver() {
180        return resolver;
181    }
182
183    public void setResolver(ContextResolver<?, CEC> resolver) {
184        this.resolver = resolver;
185    }
186
187    public boolean isOptional() {
188        return optional;
189    }
190
191    public void setOptional(boolean optional) {
192        this.optional = optional;
193    }
194
195    public String getDescription() {
196        return description;
197    }
198
199    public void setDescription(String description) {
200        this.description = description;
201    }
202
203    public String getDefaultValue() {
204        return defaultValue;
205    }
206
207    public void setDefaultValue(String defaultValue) {
208        this.defaultValue = defaultValue;
209    }
210
211    public boolean isCommandIssuer() {
212        return commandIssuer;
213    }
214
215    public void setCommandIssuer(boolean commandIssuer) {
216        this.commandIssuer = commandIssuer;
217    }
218
219    public String[] getValues() {
220        return values;
221    }
222
223    public void setValues(String[] values) {
224        this.values = values;
225    }
226
227    public Map<String, String> getFlags() {
228        return flags;
229    }
230
231    public void setFlags(Map<String, String> flags) {
232        this.flags = flags;
233    }
234
235    public boolean canConsumeInput() {
236        return canConsumeInput;
237    }
238
239    public void setCanConsumeInput(boolean canConsumeInput) {
240        this.canConsumeInput = canConsumeInput;
241    }
242
243    public void setOptionalResolver(boolean optionalResolver) {
244        this.optionalResolver = optionalResolver;
245    }
246
247    public boolean isOptionalResolver() {
248        return optionalResolver;
249    }
250
251    public boolean requiresInput() {
252        return requiresInput;
253    }
254
255    public void setRequiresInput(boolean requiresInput) {
256        this.requiresInput = requiresInput;
257    }
258
259    public String getSyntax() {
260        return syntax;
261    }
262
263    public void setSyntax(String syntax) {
264        this.syntax = syntax;
265    }
266
267    public String getConditions() {
268        return conditions;
269    }
270
271    public void setConditions(String conditions) {
272        this.conditions = conditions;
273    }
274
275    public Set<String> getRequiredPermissions() {
276        return permissions;
277    }
278}