001/*
002 * PermissionsEx
003 * Copyright (C) zml and PermissionsEx contributors
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * You may obtain a copy of the License at
008 *
009 *    http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package ca.stellardrift.permissionsex.minecraft.command.argument;
018
019import ca.stellardrift.permissionsex.minecraft.command.CommandException;
020import cloud.commandframework.arguments.parser.ArgumentParseResult;
021import cloud.commandframework.arguments.parser.ArgumentParser;
022import cloud.commandframework.context.CommandContext;
023import cloud.commandframework.exceptions.parsing.NoInputProvidedException;
024import org.checkerframework.checker.nullness.qual.NonNull;
025
026import java.util.Queue;
027import java.util.regex.Pattern;
028import java.util.regex.PatternSyntaxException;
029
030/**
031 * A parser for regular expressions.
032 *
033 * @param <C> the sender type
034 */
035public final class PatternParser<C> implements ArgumentParser<C, Pattern> {
036    private final boolean greedy;
037
038    /**
039     * Create a new parser
040     * @param greedy Get whether the argument should consume the remaining input, or just one word
041     */
042    PatternParser(final boolean greedy) {
043        this.greedy = greedy;
044    }
045
046    @Override
047    public @NonNull ArgumentParseResult<@NonNull Pattern> parse(
048        @NonNull final CommandContext<@NonNull C> commandContext,
049        @NonNull final Queue<@NonNull String> inputQueue
050    ) {
051        if (inputQueue.isEmpty()) {
052            return ArgumentParseResult.failure(new NoInputProvidedException(PatternParser.class, commandContext));
053        }
054
055        final String input;
056        if (this.greedy) {
057            input = String.join(" ", inputQueue);
058        } else {
059            // TODO: Quoted strings
060            input = inputQueue.peek();
061        }
062
063        try {
064            final Pattern result = Pattern.compile(input);
065            if (this.greedy) {
066                inputQueue.clear();
067            } else {
068                inputQueue.remove();
069            }
070            return ArgumentParseResult.success(result);
071        } catch (final PatternSyntaxException ex) {
072            return ArgumentParseResult.failure(new PatternParseException(ex));
073        }
074    }
075
076    public boolean greedy() {
077        return this.greedy;
078    }
079
080    @Override
081    public boolean isContextFree() {
082        return true;
083    }
084
085    public static final class PatternParseException extends CommandException {
086
087        private static final long serialVersionUID = -4380135510811949525L;
088
089        private final String pattern;
090        private final String error;
091        private final int index;
092
093        public PatternParseException(final PatternSyntaxException ex) {
094            super(Messages.PATTERN_ERROR_SYNTAX.tr(ex.getPattern(), ex.getIndex(), ex.getDescription()));
095            this.pattern = ex.getPattern();
096            this.error = ex.getDescription();
097            this.index = ex.getIndex();
098        }
099
100        public String pattern() {
101            return this.pattern;
102        }
103
104        public String errorDescription() {
105            return this.error;
106        }
107
108        public int index() {
109            return this.index;
110        }
111
112    }
113
114}