001//
002// MIT License
003//
004// Copyright (c) 2021 Alexander Söderberg & Contributors
005//
006// Permission is hereby granted, free of charge, to any person obtaining a copy
007// of this software and associated documentation files (the "Software"), to deal
008// in the Software without restriction, including without limitation the rights
009// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
010// copies of the Software, and to permit persons to whom the Software is
011// furnished to do so, subject to the following conditions:
012//
013// The above copyright notice and this permission notice shall be included in all
014// copies or substantial portions of the Software.
015//
016// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
017// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
018// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
019// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
020// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
021// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
022// SOFTWARE.
023//
024package cloud.commandframework.arguments;
025
026import cloud.commandframework.ArgumentDescription;
027import cloud.commandframework.Command;
028import cloud.commandframework.CommandManager;
029import cloud.commandframework.arguments.parser.ArgumentParseResult;
030import cloud.commandframework.arguments.parser.ArgumentParser;
031import cloud.commandframework.arguments.parser.ParserParameters;
032import cloud.commandframework.context.CommandContext;
033import cloud.commandframework.keys.CloudKey;
034import cloud.commandframework.keys.CloudKeyHolder;
035import cloud.commandframework.keys.SimpleCloudKey;
036import io.leangen.geantyref.TypeToken;
037import org.checkerframework.checker.nullness.qual.NonNull;
038import org.checkerframework.checker.nullness.qual.Nullable;
039
040import java.util.Collection;
041import java.util.Collections;
042import java.util.LinkedList;
043import java.util.List;
044import java.util.Objects;
045import java.util.Queue;
046import java.util.function.BiFunction;
047import java.util.regex.Pattern;
048
049/**
050 * A argument that belongs to a command
051 *
052 * @param <C> Command sender type
053 * @param <T> The type that the argument parses into
054 */
055@SuppressWarnings("unused")
056public class CommandArgument<C, T> implements Comparable<CommandArgument<?, ?>>, CloudKeyHolder<T> {
057
058    /**
059     * Pattern for command argument names
060     */
061    private static final Pattern NAME_PATTERN = Pattern.compile("[A-Za-z0-9\\-_]+");
062
063    /**
064     * A typed key representing this argument
065     */
066    private final CloudKey<T> key;
067    /**
068     * Indicates whether or not the argument is required
069     * or not. All arguments prior to any other required
070     * argument must also be required, such that the predicate
071     * (∀ c_i ∈ required)({c_0, ..., c_i-1} ⊂ required) holds true,
072     * where {c_0, ..., c_n-1} is the set of command arguments.
073     */
074    private final boolean required;
075    /**
076     * The command argument name. This might be exposed
077     * to command senders and so should be chosen carefully.
078     */
079    private final String name;
080    /**
081     * The parser that is used to parse the command input
082     * into the corresponding command type
083     */
084    private final ArgumentParser<C, T> parser;
085    /**
086     * Default value, will be empty if none was supplied
087     */
088    private final String defaultValue;
089    /**
090     * The type that is produces by the argument's parser
091     */
092    private final TypeToken<T> valueType;
093    /**
094     * Suggestion provider
095     */
096    private final BiFunction<CommandContext<C>, String, List<String>> suggestionsProvider;
097    /**
098     * Argument preprocessors that allows for extensions to existing argument types
099     * without having to update all parsers
100     */
101    private final Collection<BiFunction<@NonNull CommandContext<C>,
102            @NonNull Queue<@NonNull String>, @NonNull ArgumentParseResult<Boolean>>> argumentPreprocessors;
103
104    /**
105     * A description that will be used when registering this argument if no override is provided.
106     */
107    private final ArgumentDescription defaultDescription;
108
109    /**
110     * Whether or not the argument has been used before
111     */
112    private boolean argumentRegistered = false;
113
114    private Command<C> owningCommand;
115
116    /**
117     * Construct a new command argument
118     *
119     * @param required              Whether or not the argument is required
120     * @param name                  The argument name
121     * @param parser                The argument parser
122     * @param defaultValue          Default value used when no value is provided by the command sender
123     * @param valueType             Type produced by the parser
124     * @param suggestionsProvider   Suggestions provider
125     * @param defaultDescription    Default description to use when registering
126     * @param argumentPreprocessors Argument preprocessors
127     * @since 1.4.0
128     */
129    public CommandArgument(
130            final boolean required,
131            final @NonNull String name,
132            final @NonNull ArgumentParser<C, T> parser,
133            final @NonNull String defaultValue,
134            final @NonNull TypeToken<T> valueType,
135            final @Nullable BiFunction<CommandContext<C>, String, List<String>> suggestionsProvider,
136            final @NonNull ArgumentDescription defaultDescription,
137            final @NonNull Collection<@NonNull BiFunction<@NonNull CommandContext<C>, @NonNull Queue<@NonNull String>,
138                    @NonNull ArgumentParseResult<Boolean>>> argumentPreprocessors
139    ) {
140        this.required = required;
141        this.name = Objects.requireNonNull(name, "Name may not be null");
142        if (!NAME_PATTERN.asPredicate().test(name)) {
143            throw new IllegalArgumentException("Name must be alphanumeric");
144        }
145        this.parser = Objects.requireNonNull(parser, "Parser may not be null");
146        this.defaultValue = defaultValue;
147        this.valueType = valueType;
148        this.suggestionsProvider = suggestionsProvider == null
149                ? buildDefaultSuggestionsProvider(this)
150                : suggestionsProvider;
151        this.defaultDescription = Objects.requireNonNull(defaultDescription, "Default description may not be null");
152        this.argumentPreprocessors = new LinkedList<>(argumentPreprocessors);
153        this.key = SimpleCloudKey.of(this.name, this.valueType);
154    }
155
156    /**
157     * Construct a new command argument
158     *
159     * @param required              Whether or not the argument is required
160     * @param name                  The argument name
161     * @param parser                The argument parser
162     * @param defaultValue          Default value used when no value is provided by the command sender
163     * @param valueType             Type produced by the parser
164     * @param suggestionsProvider   Suggestions provider
165     * @param argumentPreprocessors Argument preprocessors
166     */
167    public CommandArgument(
168            final boolean required,
169            final @NonNull String name,
170            final @NonNull ArgumentParser<C, T> parser,
171            final @NonNull String defaultValue,
172            final @NonNull TypeToken<T> valueType,
173            final @Nullable BiFunction<CommandContext<C>, String, List<String>> suggestionsProvider,
174            final @NonNull Collection<@NonNull BiFunction<@NonNull CommandContext<C>, @NonNull Queue<@NonNull String>,
175                    @NonNull ArgumentParseResult<Boolean>>> argumentPreprocessors
176    ) {
177        this(
178            required,
179            name,
180            parser,
181            defaultValue,
182            valueType,
183            suggestionsProvider,
184            ArgumentDescription.empty(),
185            argumentPreprocessors
186        );
187    }
188
189    /**
190     * Construct a new command argument
191     *
192     * @param required            Whether or not the argument is required
193     * @param name                The argument name
194     * @param parser              The argument parser
195     * @param defaultValue        Default value used when no value is provided by the command sender
196     * @param valueType           Type produced by the parser
197     * @param suggestionsProvider Suggestions provider
198     */
199    public CommandArgument(
200            final boolean required,
201            final @NonNull String name,
202            final @NonNull ArgumentParser<C, T> parser,
203            final @NonNull String defaultValue,
204            final @NonNull TypeToken<T> valueType,
205            final @Nullable BiFunction<CommandContext<C>, String, List<String>> suggestionsProvider
206    ) {
207        this(required, name, parser, defaultValue, valueType, suggestionsProvider, Collections.emptyList());
208    }
209
210    /**
211     * Construct a new command argument
212     *
213     * @param required            Whether or not the argument is required
214     * @param name                The argument name
215     * @param parser              The argument parser
216     * @param defaultValue        Default value used when no value is provided by the command sender
217     * @param valueType           Type produced by the parser
218     * @param suggestionsProvider Suggestions provider
219     * @param defaultDescription    Default description to use when registering
220     * @since 1.4.0
221     */
222    public CommandArgument(
223            final boolean required,
224            final @NonNull String name,
225            final @NonNull ArgumentParser<C, T> parser,
226            final @NonNull String defaultValue,
227            final @NonNull TypeToken<T> valueType,
228            final @Nullable BiFunction<CommandContext<C>, String, List<String>> suggestionsProvider,
229            final @NonNull ArgumentDescription defaultDescription
230    ) {
231        this(required, name, parser, defaultValue, valueType, suggestionsProvider, defaultDescription, Collections.emptyList());
232    }
233
234    /**
235     * Construct a new command argument
236     *
237     * @param required            Whether or not the argument is required
238     * @param name                The argument name
239     * @param parser              The argument parser
240     * @param defaultValue        Default value used when no value is provided by the command sender
241     * @param valueType           Type produced by the parser
242     * @param suggestionsProvider Suggestions provider
243     */
244    public CommandArgument(
245            final boolean required,
246            final @NonNull String name,
247            final @NonNull ArgumentParser<C, T> parser,
248            final @NonNull String defaultValue,
249            final @NonNull Class<T> valueType,
250            final @Nullable BiFunction<@NonNull CommandContext<C>,
251                    @NonNull String, @NonNull List<@NonNull String>> suggestionsProvider
252    ) {
253        this(required, name, parser, defaultValue, TypeToken.get(valueType), suggestionsProvider);
254    }
255
256    /**
257     * Construct a new command argument
258     *
259     * @param required            Whether or not the argument is required
260     * @param name                The argument name
261     * @param parser              The argument parser
262     * @param defaultValue        Default value used when no value is provided by the command sender
263     * @param valueType           Type produced by the parser
264     * @param suggestionsProvider Suggestions provider
265     * @param defaultDescription    Default description to use when registering
266     * @since 1.4.0
267     */
268    public CommandArgument(
269            final boolean required,
270            final @NonNull String name,
271            final @NonNull ArgumentParser<C, T> parser,
272            final @NonNull String defaultValue,
273            final @NonNull Class<T> valueType,
274            final @Nullable BiFunction<@NonNull CommandContext<C>,
275                    @NonNull String, @NonNull List<@NonNull String>> suggestionsProvider,
276            final @NonNull ArgumentDescription defaultDescription
277    ) {
278        this(required, name, parser, defaultValue, TypeToken.get(valueType), suggestionsProvider, defaultDescription);
279    }
280
281    /**
282     * Construct a new command argument
283     *
284     * @param required  Whether or not the argument is required
285     * @param name      The argument name
286     * @param parser    The argument parser
287     * @param valueType Type produced by the parser
288     */
289    public CommandArgument(
290            final boolean required,
291            final @NonNull String name,
292            final @NonNull ArgumentParser<C, T> parser,
293            final @NonNull Class<T> valueType
294    ) {
295        this(required, name, parser, "", valueType, null);
296    }
297
298    private static <C> @NonNull BiFunction<@NonNull CommandContext<C>, @NonNull String,
299            @NonNull List<String>> buildDefaultSuggestionsProvider(final @NonNull CommandArgument<C, ?> argument) {
300        return new DelegatingSuggestionsProvider<>(argument.getName(), argument.getParser());
301    }
302
303    /**
304     * Create a new command argument
305     *
306     * @param clazz Argument class
307     * @param name  Argument name
308     * @param <C>   Command sender type
309     * @param <T>   Argument Type. Used to make the compiler happy.
310     * @return Argument builder
311     */
312    public static <C, T> CommandArgument.@NonNull Builder<C, T> ofType(
313            final @NonNull TypeToken<T> clazz,
314            final @NonNull String name
315    ) {
316        return new Builder<>(clazz, name);
317    }
318
319    /**
320     * Create a new command argument
321     *
322     * @param clazz Argument class
323     * @param name  Argument name
324     * @param <C>   Command sender type
325     * @param <T>   Argument Type. Used to make the compiler happy.
326     * @return Argument builder
327     */
328    public static <C, T> CommandArgument.@NonNull Builder<@NonNull C, @NonNull T> ofType(
329            final @NonNull Class<T> clazz,
330            final @NonNull String name
331    ) {
332        return new Builder<>(TypeToken.get(clazz), name);
333    }
334
335    @Override
336    public final @NonNull CloudKey<T> getKey() {
337        return this.key;
338    }
339
340    /**
341     * Check whether or not the command argument is required
342     *
343     * @return {@code true} if the argument is required, {@code false} if not
344     */
345    public boolean isRequired() {
346        return this.required;
347    }
348
349    /**
350     * Get the command argument name;
351     *
352     * @return Argument name
353     */
354    public @NonNull String getName() {
355        return this.name;
356    }
357
358    /**
359     * Get the parser that is used to parse the command input
360     * into the corresponding command type
361     *
362     * @return Command parser
363     */
364    public @NonNull ArgumentParser<C, T> getParser() {
365        return this.parser;
366    }
367
368    @Override
369    public final @NonNull String toString() {
370        return String.format("%s{name=%s}", this.getClass().getSimpleName(), this.name);
371    }
372
373    /**
374     * Register a new preprocessor. If all preprocessor has succeeding {@link ArgumentParseResult results}
375     * that all return {@code true}, the argument will be passed onto the parser.
376     * <p>
377     * It is important that the preprocessor doesn't pop any input. Instead, it should only peek.
378     *
379     * @param preprocessor Preprocessor
380     * @return {@code this}
381     */
382    public @NonNull CommandArgument<C, T> addPreprocessor(
383            final @NonNull BiFunction<@NonNull CommandContext<C>, @NonNull Queue<String>,
384                    @NonNull ArgumentParseResult<Boolean>> preprocessor
385    ) {
386        this.argumentPreprocessors.add(preprocessor);
387        return this;
388    }
389
390    /**
391     * Preprocess command input. This will immediately forward any failed argument parse results.
392     * If none fails, a {@code true} result will be returned
393     *
394     * @param context Command context
395     * @param input   Remaining command input. None will be popped
396     * @return Parsing error, or argument containing {@code true}
397     */
398    public @NonNull ArgumentParseResult<Boolean> preprocess(
399            final @NonNull CommandContext<C> context,
400            final @NonNull Queue<String> input
401    ) {
402        for (final BiFunction<@NonNull CommandContext<C>, @NonNull Queue<String>,
403                @NonNull ArgumentParseResult<Boolean>> preprocessor : this.argumentPreprocessors) {
404            final ArgumentParseResult<Boolean> result = preprocessor.apply(
405                    context,
406                    input
407            );
408            if (result.getFailure().isPresent()) {
409                return result;
410            }
411        }
412        return ArgumentParseResult.success(true);
413    }
414
415    /**
416     * Get the owning command
417     *
418     * @return Owning command
419     */
420    @Nullable
421    public Command<C> getOwningCommand() {
422        return this.owningCommand;
423    }
424
425    /**
426     * Set the owning command
427     *
428     * @param owningCommand Owning command
429     */
430    public void setOwningCommand(final @NonNull Command<C> owningCommand) {
431        if (this.owningCommand != null) {
432            throw new IllegalStateException("Cannot replace owning command");
433        }
434        this.owningCommand = owningCommand;
435    }
436
437    /**
438     * Get the argument suggestions provider
439     *
440     * @return Suggestions provider
441     */
442    public final @NonNull BiFunction<@NonNull CommandContext<C>, @NonNull String,
443            @NonNull List<String>> getSuggestionsProvider() {
444        return this.suggestionsProvider;
445    }
446
447    /**
448     * Get the default description to use when registering and no other is provided.
449     *
450     * @return the default description
451     */
452    public final @NonNull ArgumentDescription getDefaultDescription() {
453        return this.defaultDescription;
454    }
455
456    @Override
457    public final boolean equals(final Object o) {
458        if (this == o) {
459            return true;
460        }
461        if (o == null || getClass() != o.getClass()) {
462            return false;
463        }
464        final CommandArgument<?, ?> that = (CommandArgument<?, ?>) o;
465        return isRequired() == that.isRequired() && Objects.equals(getName(), that.getName());
466    }
467
468    @Override
469    public final int hashCode() {
470        return Objects.hash(isRequired(), getName());
471    }
472
473    @Override
474    public final int compareTo(final @NonNull CommandArgument<?, ?> o) {
475        if (this instanceof StaticArgument) {
476            if (o instanceof StaticArgument) {
477                return (this.getName().compareTo(o.getName()));
478            } else {
479                return -1;
480            }
481        } else {
482            if (o instanceof StaticArgument) {
483                return 1;
484            } else {
485                return 0;
486            }
487        }
488    }
489
490    /**
491     * Get the default value
492     *
493     * @return Default value
494     */
495    public @NonNull String getDefaultValue() {
496        return this.defaultValue;
497    }
498
499    /**
500     * Check if the argument has a default value
501     *
502     * @return {@code true} if the argument has a default value, {@code false} if not
503     */
504    public boolean hasDefaultValue() {
505        return !this.isRequired()
506                && !this.getDefaultValue().isEmpty();
507    }
508
509    /**
510     * Get the type of this argument's value
511     *
512     * @return Value type
513     */
514    public @NonNull TypeToken<T> getValueType() {
515        return this.valueType;
516    }
517
518    /**
519     * Create a copy of the command argument
520     *
521     * @return Copied argument
522     */
523    public @NonNull CommandArgument<C, T> copy() {
524        CommandArgument.Builder<C, T> builder = ofType(this.valueType, this.name);
525        builder = builder.withSuggestionsProvider(this.suggestionsProvider);
526        builder = builder.withParser(this.parser);
527        if (this.isRequired()) {
528            builder = builder.asRequired();
529        } else if (this.defaultValue.isEmpty()) {
530            builder = builder.asOptional();
531        } else {
532            builder = builder.asOptionalWithDefault(this.defaultValue);
533        }
534        builder = builder.withDefaultDescription(this.defaultDescription);
535
536        return builder.build();
537    }
538
539    /**
540     * Check whether or not the argument has been used in a command
541     *
542     * @return {@code true} if the argument has been used in a command, else {@code false}
543     */
544    public boolean isArgumentRegistered() {
545        return this.argumentRegistered;
546    }
547
548    /**
549     * Indicate that the argument has been associated with a command
550     */
551    public void setArgumentRegistered() {
552        this.argumentRegistered = true;
553    }
554
555
556    /**
557     * Mutable builder for {@link CommandArgument} instances
558     *
559     * @param <C> Command sender type
560     * @param <T> Argument value type
561     */
562    public static class Builder<C, T> {
563
564        private final TypeToken<T> valueType;
565        private final String name;
566
567        private CommandManager<C> manager;
568        private boolean required = true;
569        private ArgumentParser<C, T> parser;
570        private String defaultValue = "";
571        private BiFunction<@NonNull CommandContext<C>, @NonNull String, @NonNull List<String>> suggestionsProvider;
572        private @NonNull ArgumentDescription defaultDescription = ArgumentDescription.empty();
573
574        private final Collection<BiFunction<@NonNull CommandContext<C>,
575                @NonNull String, @NonNull ArgumentParseResult<Boolean>>> argumentPreprocessors = new LinkedList<>();
576
577        protected Builder(
578                final @NonNull TypeToken<T> valueType,
579                final @NonNull String name
580        ) {
581            this.valueType = valueType;
582            this.name = name;
583        }
584
585        protected Builder(
586                final @NonNull Class<T> valueType,
587                final @NonNull String name
588        ) {
589            this(TypeToken.get(valueType), name);
590        }
591
592        /**
593         * Set the command manager. Will be used to create a default parser
594         * if none was provided
595         *
596         * @param manager Command manager
597         * @return Builder instance
598         */
599        public @NonNull Builder<@NonNull C, @NonNull T> manager(final @NonNull CommandManager<C> manager) {
600            this.manager = manager;
601            return this;
602        }
603
604        /**
605         * Indicates that the argument is required.
606         * All arguments prior to any other required
607         * argument must also be required, such that the predicate
608         * (∀ c_i ∈ required)({c_0, ..., c_i-1} ⊂ required) holds true,
609         * where {c_0, ..., c_n-1} is the set of command arguments.
610         *
611         * @return Builder instance
612         */
613        public @NonNull Builder<@NonNull C, @NonNull T> asRequired() {
614            this.required = true;
615            return this;
616        }
617
618        /**
619         * Indicates that the argument is optional.
620         * All arguments prior to any other required
621         * argument must also be required, such that the predicate
622         * (∀ c_i ∈ required)({c_0, ..., c_i-1} ⊂ required) holds true,
623         * where {c_0, ..., c_n-1} is the set of command arguments.
624         *
625         * @return Builder instance
626         */
627        public @NonNull Builder<@NonNull C, @NonNull T> asOptional() {
628            this.required = false;
629            return this;
630        }
631
632        /**
633         * Indicates that the argument is optional.
634         * All arguments prior to any other required
635         * argument must also be required, such that the predicate
636         * (∀ c_i ∈ required)({c_0, ..., c_i-1} ⊂ required) holds true,
637         * where {c_0, ..., c_n-1} is the set of command arguments.
638         *
639         * @param defaultValue Default value that will be used if none was supplied
640         * @return Builder instance
641         */
642        public @NonNull Builder<@NonNull C, @NonNull T> asOptionalWithDefault(final @NonNull String defaultValue) {
643            this.defaultValue = defaultValue;
644            this.required = false;
645            return this;
646        }
647
648        /**
649         * Set the argument parser
650         *
651         * @param parser Argument parser
652         * @return Builder instance
653         */
654        public @NonNull Builder<@NonNull C, @NonNull T> withParser(final @NonNull ArgumentParser<@NonNull C, @NonNull T> parser) {
655            this.parser = Objects.requireNonNull(parser, "Parser may not be null");
656            return this;
657        }
658
659        /**
660         * Set the suggestions provider
661         *
662         * @param suggestionsProvider Suggestions provider
663         * @return Builder instance
664         */
665        public @NonNull Builder<@NonNull C, @NonNull T> withSuggestionsProvider(
666                final @NonNull BiFunction<@NonNull CommandContext<C>,
667                        @NonNull String, @NonNull List<String>> suggestionsProvider
668        ) {
669            this.suggestionsProvider = suggestionsProvider;
670            return this;
671        }
672
673        /**
674         * Set the default description to be used for this argument.
675         *
676         * <p>The default description is used when no other description is provided for a certain argument.</p>
677         *
678         * @param defaultDescription The default description
679         * @return Builder instance
680         * @since 1.4.0
681         */
682        public @NonNull Builder<@NonNull C, @NonNull T> withDefaultDescription(
683                final @NonNull ArgumentDescription defaultDescription
684        ) {
685            this.defaultDescription = Objects.requireNonNull(defaultDescription, "Default description may not be null");
686            return this;
687        }
688
689        /**
690         * Construct a command argument from the builder settings
691         *
692         * @return Constructed argument
693         */
694        public @NonNull CommandArgument<@NonNull C, @NonNull T> build() {
695            if (this.parser == null && this.manager != null) {
696                this.parser = this.manager.getParserRegistry().createParser(valueType, ParserParameters.empty())
697                        .orElse(null);
698            }
699            if (this.parser == null) {
700                this.parser = (c, i) -> ArgumentParseResult
701                        .failure(new UnsupportedOperationException("No parser was specified"));
702            }
703            if (this.suggestionsProvider == null) {
704                this.suggestionsProvider = new DelegatingSuggestionsProvider<>(this.name, this.parser);
705            }
706            return new CommandArgument<>(
707                    this.required,
708                    this.name,
709                    this.parser,
710                    this.defaultValue,
711                    this.valueType,
712                    this.suggestionsProvider,
713                    this.defaultDescription
714            );
715        }
716
717        protected final @NonNull String getName() {
718            return this.name;
719        }
720
721        protected final boolean isRequired() {
722            return this.required;
723        }
724
725        protected final @NonNull ArgumentParser<@NonNull C, @NonNull T> getParser() {
726            return this.parser;
727        }
728
729        protected final @NonNull String getDefaultValue() {
730            return this.defaultValue;
731        }
732
733        protected final @NonNull BiFunction<@NonNull CommandContext<C>, @NonNull String, @NonNull List<String>>
734        getSuggestionsProvider() {
735            return this.suggestionsProvider;
736        }
737
738        protected final @NonNull ArgumentDescription getDefaultDescription() {
739            return this.defaultDescription;
740        }
741
742    }
743
744}