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.context.ContextDefinition; 020import ca.stellardrift.permissionsex.context.ContextValue; 021import ca.stellardrift.permissionsex.impl.util.PCollections; 022import ca.stellardrift.permissionsex.minecraft.MinecraftPermissionsEx; 023import ca.stellardrift.permissionsex.minecraft.command.CommandException; 024import ca.stellardrift.permissionsex.minecraft.command.Commander; 025import ca.stellardrift.permissionsex.minecraft.command.PEXCommandPreprocessor; 026import ca.stellardrift.permissionsex.subject.CalculatedSubject; 027import ca.stellardrift.permissionsex.subject.SubjectRef; 028import cloud.commandframework.arguments.parser.ArgumentParseResult; 029import cloud.commandframework.arguments.parser.ArgumentParser; 030import cloud.commandframework.context.CommandContext; 031import cloud.commandframework.exceptions.parsing.NoInputProvidedException; 032import org.checkerframework.checker.nullness.qual.NonNull; 033import org.checkerframework.checker.nullness.qual.Nullable; 034 035import java.util.Collections; 036import java.util.List; 037import java.util.Queue; 038import java.util.regex.Pattern; 039import java.util.stream.Collectors; 040 041public final class ContextValueParser implements ArgumentParser<Commander, ContextValue<?>> { 042 private static final Pattern CONTEXT_SPLIT = Pattern.compile("="); 043 044 ContextValueParser() { 045 } 046 047 @Override 048 public @NonNull ArgumentParseResult<@NonNull ContextValue<?>> parse( 049 final @NonNull CommandContext<@NonNull Commander> ctx, 050 final @NonNull Queue<@NonNull String> queue 051 ) { 052 final @Nullable String provided = queue.peek(); 053 if (provided == null) { 054 return ArgumentParseResult.failure(new NoInputProvidedException(ContextValueParser.class, ctx)); 055 } 056 final String[] contexts = CONTEXT_SPLIT.split(provided, 2); 057 if (contexts.length != 2) { 058 return ArgumentParseResult.failure(new CommandException(Messages.CONTEXT_ERROR_FORMAT.tr())); 059 } 060 061 final @Nullable ContextDefinition<?> definition = ctx.<MinecraftPermissionsEx<?>>get(PEXCommandPreprocessor.PEX_MANAGER).engine() 062 .contextDefinition(contexts[0]); 063 064 if (definition == null) { 065 return ArgumentParseResult.failure(new CommandException(Messages.CONTEXT_ERROR_TYPE.tr(contexts[0]))); 066 } 067 068 final ArgumentParseResult<ContextValue<?>> result = toContextValue(definition, contexts[1]); 069 if (result.getParsedValue().isPresent()) { 070 queue.remove(); 071 } 072 return result; 073 } 074 075 private <V> ArgumentParseResult<ContextValue<?>> toContextValue(final ContextDefinition<V> definition, final String input) { 076 final @Nullable V value = definition.deserialize(input); 077 078 if (value == null) { 079 return ArgumentParseResult.failure(new CommandException(Messages.CONTEXT_ERROR_VALUE.tr(definition.name()))); 080 } else { 081 return ArgumentParseResult.success(definition.createValue(value)); 082 } 083 } 084 085 @Override 086 public @NonNull List<@NonNull String> suggestions(final @NonNull CommandContext<Commander> ctx, final @NonNull String input) { 087 final MinecraftPermissionsEx<?> manager = ctx.get(PEXCommandPreprocessor.PEX_MANAGER); 088 final String[] split = CONTEXT_SPLIT.split(input, 2); 089 if (split.length < 2) { // before = 090 return manager.engine().registeredContextTypes().stream() 091 .map(def -> def.name() + "=") 092 .collect(Collectors.toList()); 093 } else { // <fully written type>=<partial value 094 final @Nullable ContextDefinition<?> definition = manager.engine().contextDefinition(split[0]); 095 final @Nullable SubjectRef<?> senderId = ctx.getSender().subjectIdentifier(); 096 if (definition == null || senderId == null) { 097 return Collections.emptyList(); 098 } 099 100 return serializeSuggestions(definition, manager.engine().subject(senderId).join()); 101 } 102 } 103 104 private <V> List<String> serializeSuggestions(final ContextDefinition<V> definition, final CalculatedSubject subject) { 105 return PCollections.asVector(definition.suggestValues(subject), ent -> definition.name() + "=" + definition.serialize(ent)); 106 } 107 108 @Override 109 public boolean isContextFree() { 110 return true; 111 } 112 113}