use of org.spongepowered.common.command.parameter.multi.SpongeMultiParameter in project SpongeCommon by SpongePowered.
the class SpongeParameterTranslator method createAndAttachNode.
private Collection<? extends CommandNode<CommandSourceStack>> createAndAttachNode(final Collection<? extends CommandNode<CommandSourceStack>> parents, final List<Parameter> children, @Nullable final SpongeCommandExecutorWrapper executorWrapper, final boolean shouldTerminate, final boolean allowSubcommands) {
final Set<CommandNode<CommandSourceStack>> nodesToAttachTo = new HashSet<>(parents);
final ListIterator<Parameter> parameterIterator = children.listIterator();
while (parameterIterator.hasNext()) {
final Parameter parameter = parameterIterator.next();
final boolean hasNext = parameterIterator.hasNext();
if (parameter instanceof Parameter.Subcommand) {
final Parameter.Subcommand subcommands = ((Parameter.Subcommand) parameter);
if (!allowSubcommands) {
throw new IllegalStateException("Subcommands are not allowed for this element (subcommands were " + String.join(", ", subcommands.aliases() + ")!"));
}
if (hasNext) {
// this is a failure condition because there cannot be a parameter after a subcommand.
throw new IllegalStateException("A parameter cannot be placed after a subcommand parameter!");
}
// If the child is a subcommand, get the subcommand and attach it. At this point,
// we're done with the chain and we break out.
final Collection<LiteralCommandNode<CommandSourceStack>> nodes = this.createCommandTree(subcommands.command(), subcommands.aliases());
for (final LiteralCommandNode<CommandSourceStack> node : nodes) {
nodesToAttachTo.forEach(x -> x.addChild(node));
}
// No further attaching should be done in this scenario
return Collections.emptyList();
} else {
final boolean isOptional;
final boolean isTerminal;
final Collection<CommandNode<CommandSourceStack>> parametersToAttachTo;
if (parameter instanceof SpongeMultiParameter) {
isOptional = parameter.isOptional();
isTerminal = !hasNext || parameter.isTerminal();
// In these cases, we delegate to the parameter, which may return here.
if (parameter instanceof SpongeFirstOfParameter) {
// take each parameter in turn and evaluate it, returns the terminal nodes of each block
parametersToAttachTo = new ArrayList<>();
for (final Parameter p : ((SpongeFirstOfParameter) parameter).childParameters()) {
final Collection<? extends CommandNode<CommandSourceStack>> branchNodesToAttachTo = this.createAndAttachNode(nodesToAttachTo, Collections.singletonList(p), executorWrapper, isTerminal, allowSubcommands);
parametersToAttachTo.addAll(branchNodesToAttachTo);
}
} else {
// not so fancy stuff, it's a sequence, returns the terminal nodes of the block
parametersToAttachTo = new ArrayList<>(this.createAndAttachNode(nodesToAttachTo, ((SpongeMultiParameter) parameter).childParameters(), executorWrapper, isTerminal, allowSubcommands));
}
} else {
// We have a Parameter.Value
parametersToAttachTo = new ArrayList<>();
final SpongeParameterValue<?> valueParameter = (SpongeParameterValue<?>) parameter;
final boolean isConsumeAll = valueParameter.willConsumeAllRemaining();
if (isConsumeAll && hasNext) {
// this should not happen.
throw new IllegalStateException("A parameter that consumes all must be at the end of a parameter chain.");
}
isOptional = valueParameter.isOptional();
isTerminal = (shouldTerminate && !hasNext) || valueParameter.isTerminal();
// Process the next element if it exists
StringBuilder suffix = null;
final Set<String> names = nodesToAttachTo.stream().flatMap(x -> x.getChildren().stream()).map(CommandNode::getName).collect(Collectors.toSet());
String key = valueParameter.key().key();
while (names.contains(key)) {
if (suffix == null) {
suffix = new StringBuilder(String.valueOf(names.size()));
} else {
suffix.append("_").append(names.size());
}
// prevents duplication
key = key + suffix;
}
final SpongeArgumentCommandNodeBuilder<?> thisNode = SpongeParameterTranslator.createArgumentNodeBuilders(valueParameter, suffix == null ? null : suffix.toString());
if (isTerminal) {
thisNode.executes(executorWrapper);
}
// Apply the node to the parent.
final CommandNode<CommandSourceStack> builtNode = thisNode.build();
if (isConsumeAll) {
// the built child will return to the previous node, which will allow this to be called again.
builtNode.addChild(thisNode.redirect(builtNode).build());
}
parametersToAttachTo.add(builtNode);
// Make sure the nodes we need to attach to have the nodes we need to
nodesToAttachTo.forEach(x -> x.addChild(builtNode));
}
// If this is not optional, then we clear the "toAttachTo" list because we do not want to skip the parameter.
if (!isOptional) {
nodesToAttachTo.clear();
}
nodesToAttachTo.addAll(parametersToAttachTo);
}
}
// If we should make any terminal parameters actually terminal, we do that now.
if (shouldTerminate) {
for (final CommandNode<CommandSourceStack> node : nodesToAttachTo) {
// These are therefore terminal.
if (node instanceof SpongeNode) {
// they should be, but just in case
((SpongeNode) node).forceExecutor(executorWrapper);
}
}
}
return nodesToAttachTo;
}
Aggregations