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; 025import org.checkerframework.checker.nullness.qual.Nullable; 026 027import java.util.Queue; 028 029public final class OptionValueParser<C> implements ArgumentParser<C, String> { 030 private static final char ESCAPE = '\\'; 031 private static final String FLAG_STARTER = "-"; 032 033 OptionValueParser() { 034 } 035 036 @Override 037 public @NonNull ArgumentParseResult<@NonNull String> parse( 038 @NonNull final CommandContext<@NonNull C> commandContext, 039 @NonNull final Queue<@NonNull String> inputQueue 040 ) { 041 final @Nullable String input = inputQueue.peek(); 042 if (input == null || input.startsWith(FLAG_STARTER)) { 043 return ArgumentParseResult.failure(new NoInputProvidedException( 044 PermissionValueParser.class, 045 commandContext 046 )); 047 } 048 049 final char startChar = input.charAt(0); 050 051 // If quoted string: read until a word ending with a quote 052 if (startChar == '\'' || startChar == '\"') { 053 final StringBuilder result = new StringBuilder(); 054 result.append(inputQueue.remove(), 1, input.length()); 055 @Nullable String next; 056 while ((next = inputQueue.peek()) != null) { 057 result.append(" "); 058 if (next.length() > 0 && next.charAt(next.length() - 1) == startChar) { 059 // We've found the end of a quoted string, maybe 060 // if escaped, append without escape then continue 061 if (next.charAt(next.length() - 1) == ESCAPE) { 062 result.append(inputQueue.remove(), 0, next.length() - 2) 063 .append(startChar); 064 continue; 065 } else { 066 result.append(inputQueue.remove(), 0, next.length() - 1); 067 // then return our full quoted string 068 return ArgumentParseResult.success(result.toString()); 069 } 070 } 071 result.append(inputQueue.remove()); 072 } 073 074 // If we made it to the end without finding an end quote, throw an error 075 return ArgumentParseResult.failure(new CommandException(Messages.OPTION_VALUE_ERROR_QUOTE_UNTERMINATED.tr())); 076 } else { // otherwise, read until end of line, or a word starting with '-' 077 final StringBuilder result = new StringBuilder(); 078 result.append(inputQueue.remove()); 079 @Nullable String next; 080 while ((next = inputQueue.peek()) != null) { 081 if (next.startsWith(FLAG_STARTER)) { 082 break; 083 } 084 result.append(" ").append(inputQueue.remove()); 085 } 086 087 return ArgumentParseResult.success(result.toString()); 088 } 089 } 090 091 @Override 092 public boolean isContextFree() { 093 return true; 094 } 095 096}