Search in sources :

Example 1 with RootNode

use of net.kyori.adventure.text.minimessage.internal.parser.node.RootNode in project adventure by KyoriPowered.

the class MiniMessageParser method parseToTree.

@NotNull
RootNode parseToTree(@NotNull final ContextImpl context) {
    final TagResolver combinedResolver = TagResolver.resolver(this.tagResolver, context.extraTags());
    final String processedMessage = context.preProcessor().apply(context.message());
    final Consumer<String> debug = context.debugOutput();
    if (debug != null) {
        debug.accept("Beginning parsing message ");
        debug.accept(processedMessage);
        debug.accept("\n");
    }
    final TokenParser.TagProvider transformationFactory;
    if (debug != null) {
        transformationFactory = (name, args, token) -> {
            try {
                debug.accept("Attempting to match node '");
                debug.accept(name);
                debug.accept("'");
                if (token != null) {
                    debug.accept(" at column ");
                    debug.accept(String.valueOf(token.startIndex()));
                }
                debug.accept("\n");
                @Nullable final Tag transformation = combinedResolver.resolve(name, new ArgumentQueueImpl<>(context, args), context);
                if (transformation == null) {
                    debug.accept("Could not match node '");
                    debug.accept(name);
                    debug.accept("'\n");
                } else {
                    debug.accept("Successfully matched node '");
                    debug.accept(name);
                    debug.accept("' to tag ");
                    debug.accept(transformation instanceof Examinable ? ((Examinable) transformation).examinableName() : transformation.getClass().getName());
                    debug.accept("\n");
                }
                return transformation;
            } catch (final ParsingException e) {
                if (token != null && e instanceof ParsingExceptionImpl) {
                    final ParsingExceptionImpl impl = (ParsingExceptionImpl) e;
                    if (impl.tokens().length == 0) {
                        impl.tokens(new Token[] { token });
                    }
                }
                debug.accept("Could not match node '");
                debug.accept(name);
                debug.accept("' - ");
                debug.accept(e.getMessage());
                debug.accept("\n");
                return null;
            }
        };
    } else {
        transformationFactory = (name, args, token) -> {
            try {
                return combinedResolver.resolve(name, new ArgumentQueueImpl<>(context, args), context);
            } catch (final ParsingException ignored) {
                return null;
            }
        };
    }
    final Predicate<String> tagNameChecker = name -> {
        final String sanitized = TokenParser.TagProvider.sanitizePlaceholderName(name);
        return combinedResolver.has(sanitized);
    };
    final String preProcessed = TokenParser.resolvePreProcessTags(processedMessage, transformationFactory);
    context.message(preProcessed);
    // Then, once MiniMessage placeholders have been inserted, we can do the real parse
    final RootNode root = TokenParser.parse(transformationFactory, tagNameChecker, preProcessed, processedMessage, context.strict());
    if (debug != null) {
        debug.accept("Text parsed into element tree:\n");
        debug.accept(root.toString());
    }
    return root;
}
Also used : Tag(net.kyori.adventure.text.minimessage.tag.Tag) ElementNode(net.kyori.adventure.text.minimessage.internal.parser.node.ElementNode) Token(net.kyori.adventure.text.minimessage.internal.parser.Token) RootNode(net.kyori.adventure.text.minimessage.internal.parser.node.RootNode) Predicate(java.util.function.Predicate) TagNode(net.kyori.adventure.text.minimessage.internal.parser.node.TagNode) Collectors(java.util.stream.Collectors) ParsingExceptionImpl(net.kyori.adventure.text.minimessage.internal.parser.ParsingExceptionImpl) ArrayList(java.util.ArrayList) Objects(java.util.Objects) Consumer(java.util.function.Consumer) Examinable(net.kyori.examination.Examinable) Nullable(org.jetbrains.annotations.Nullable) List(java.util.List) TokenParser(net.kyori.adventure.text.minimessage.internal.parser.TokenParser) Component(net.kyori.adventure.text.Component) MultiLineStringExaminer(net.kyori.examination.string.MultiLineStringExaminer) BiConsumer(java.util.function.BiConsumer) TokenType(net.kyori.adventure.text.minimessage.internal.parser.TokenType) ValueNode(net.kyori.adventure.text.minimessage.internal.parser.node.ValueNode) NotNull(org.jetbrains.annotations.NotNull) Modifying(net.kyori.adventure.text.minimessage.tag.Modifying) Inserting(net.kyori.adventure.text.minimessage.tag.Inserting) TagResolver(net.kyori.adventure.text.minimessage.tag.resolver.TagResolver) RootNode(net.kyori.adventure.text.minimessage.internal.parser.node.RootNode) TagResolver(net.kyori.adventure.text.minimessage.tag.resolver.TagResolver) TokenParser(net.kyori.adventure.text.minimessage.internal.parser.TokenParser) Token(net.kyori.adventure.text.minimessage.internal.parser.Token) Tag(net.kyori.adventure.text.minimessage.tag.Tag) ParsingExceptionImpl(net.kyori.adventure.text.minimessage.internal.parser.ParsingExceptionImpl) Nullable(org.jetbrains.annotations.Nullable) Examinable(net.kyori.examination.Examinable) NotNull(org.jetbrains.annotations.NotNull)

Example 2 with RootNode

use of net.kyori.adventure.text.minimessage.internal.parser.node.RootNode in project adventure by KyoriPowered.

the class TokenParser method buildTree.

/*
   * Build a tree from the OPEN_TAG and CLOSE_TAG tokens
   */
private static RootNode buildTree(@NotNull final TagProvider tagProvider, @NotNull final Predicate<String> tagNameChecker, @NotNull final List<Token> tokens, @NotNull final String message, @NotNull final String originalMessage, final boolean strict) throws ParsingException {
    final RootNode root = new RootNode(message, originalMessage);
    ElementNode node = root;
    for (final Token token : tokens) {
        final TokenType type = token.type();
        switch(type) {
            case TEXT:
                node.addChild(new TextNode(node, token, message));
                break;
            case OPEN_TAG:
            case OPEN_CLOSE_TAG:
                // Check if this even is a valid tag
                final Token tagNamePart = token.childTokens().get(0);
                final String tagName = message.substring(tagNamePart.startIndex(), tagNamePart.endIndex());
                if (!TagInternals.sanitizeAndCheckValidTagName(tagName)) {
                    // This wouldn't be a valid tag, just parse it as text instead!
                    node.addChild(new TextNode(node, token, message));
                    break;
                }
                final TagNode tagNode = new TagNode(node, token, message, tagProvider);
                if (tagNameChecker.test(tagNode.name())) {
                    final Tag tag = tagProvider.resolve(tagNode);
                    if (tag == null) {
                        // something went wrong, ignore it
                        // if strict mode is enabled this will throw an exception for us
                        node.addChild(new TextNode(node, token, message));
                    } else if (tag == ParserDirective.RESET) {
                        // instead, they close all currently open tags
                        if (strict) {
                            throw new ParsingExceptionImpl("<reset> tags are not allowed when strict mode is enabled", message, token);
                        }
                        node = root;
                    } else {
                        // This is a recognized tag, goes in the tree
                        tagNode.tag(tag);
                        node.addChild(tagNode);
                        if (type != TokenType.OPEN_CLOSE_TAG && (!(tag instanceof Inserting) || ((Inserting) tag).allowsChildren())) {
                            node = tagNode;
                        }
                    }
                } else {
                    // not recognized, plain text
                    node.addChild(new TextNode(node, token, message));
                }
                // OPEN_TAG
                break;
            case CLOSE_TAG:
                final List<Token> childTokens = token.childTokens();
                if (childTokens.isEmpty()) {
                    throw new IllegalStateException("CLOSE_TAG token somehow has no children - " + "the parser should not allow this. Original text: " + message);
                }
                final ArrayList<String> closeValues = new ArrayList<>(childTokens.size());
                for (final Token childToken : childTokens) {
                    closeValues.add(TagPart.unquoteAndEscape(message, childToken.startIndex(), childToken.endIndex()));
                }
                final String closeTagName = closeValues.get(0);
                if (tagNameChecker.test(closeTagName)) {
                    final Tag tag = tagProvider.resolve(closeTagName);
                    if (tag == ParserDirective.RESET) {
                        // This is a synthetic node, closing it means nothing in the context of building a tree
                        continue;
                    }
                } else {
                    // tag does not exist, so treat it as text
                    node.addChild(new TextNode(node, token, message));
                    continue;
                }
                ElementNode parentNode = node;
                while (parentNode instanceof TagNode) {
                    final List<TagPart> openParts = ((TagNode) parentNode).parts();
                    if (tagCloses(closeValues, openParts)) {
                        if (parentNode != node && strict) {
                            final String msg = "Unclosed tag encountered; " + ((TagNode) node).name() + " is not closed, because " + closeValues.get(0) + " was closed first.";
                            throw new ParsingExceptionImpl(msg, message, parentNode.token(), node.token(), token);
                        }
                        final ElementNode par = parentNode.parent();
                        if (par != null) {
                            node = par;
                        } else {
                            throw new IllegalStateException("Root node matched with close tag value, " + "this should not be possible. Original text: " + message);
                        }
                        break;
                    }
                    parentNode = parentNode.parent();
                }
                if (parentNode == null || parentNode instanceof RootNode) {
                    // This means the closing tag didn't match to anything
                    // Since open tags which don't match to anything is never an error, neither is this
                    node.addChild(new TextNode(node, token, message));
                    break;
                }
                // CLOSE_TAG
                break;
            default:
                // ignore other tags
                break;
        }
    }
    if (strict && root != node) {
        final ArrayList<TagNode> openTags = new ArrayList<>();
        {
            ElementNode n = node;
            while (n != null) {
                if (n instanceof TagNode) {
                    openTags.add((TagNode) n);
                } else {
                    break;
                }
                n = n.parent();
            }
        }
        final Token[] errorTokens = new Token[openTags.size()];
        final StringBuilder sb = new StringBuilder("All tags must be explicitly closed while in strict mode. " + "End of string found with open tags: ");
        int i = 0;
        final ListIterator<TagNode> iter = openTags.listIterator(openTags.size());
        while (iter.hasPrevious()) {
            final TagNode n = iter.previous();
            errorTokens[i++] = n.token();
            sb.append(n.name());
            if (iter.hasPrevious()) {
                sb.append(", ");
            }
        }
        throw new ParsingExceptionImpl(sb.toString(), message, errorTokens);
    }
    return root;
}
Also used : RootNode(net.kyori.adventure.text.minimessage.internal.parser.node.RootNode) ArrayList(java.util.ArrayList) TextNode(net.kyori.adventure.text.minimessage.internal.parser.node.TextNode) ElementNode(net.kyori.adventure.text.minimessage.internal.parser.node.ElementNode) Inserting(net.kyori.adventure.text.minimessage.tag.Inserting) TagPart(net.kyori.adventure.text.minimessage.internal.parser.node.TagPart) Tag(net.kyori.adventure.text.minimessage.tag.Tag) TagNode(net.kyori.adventure.text.minimessage.internal.parser.node.TagNode)

Example 3 with RootNode

use of net.kyori.adventure.text.minimessage.internal.parser.node.RootNode in project adventure by KyoriPowered.

the class MiniMessageParser method parseToTree.

@NotNull
RootNode parseToTree(@NotNull final String richMessage, @NotNull final ContextImpl context) {
    final TagResolver combinedResolver = TagResolver.resolver(this.tagResolver, context.extraTags());
    final Consumer<String> debug = context.debugOutput();
    if (debug != null) {
        debug.accept("Beginning parsing message ");
        debug.accept(richMessage);
        debug.accept("\n");
    }
    final TokenParser.TagProvider transformationFactory;
    if (debug != null) {
        transformationFactory = (name, args, token) -> {
            try {
                debug.accept("Attempting to match node '");
                debug.accept(name);
                debug.accept("'");
                if (token != null) {
                    debug.accept(" at column ");
                    debug.accept(String.valueOf(token.startIndex()));
                }
                debug.accept("\n");
                @Nullable final Tag transformation = combinedResolver.resolve(name, new ArgumentQueueImpl<>(context, args), context);
                if (transformation == null) {
                    debug.accept("Could not match node '");
                    debug.accept(name);
                    debug.accept("'\n");
                } else {
                    debug.accept("Successfully matched node '");
                    debug.accept(name);
                    debug.accept("' to tag ");
                    debug.accept(transformation instanceof Examinable ? ((Examinable) transformation).examinableName() : transformation.getClass().getName());
                    debug.accept("\n");
                }
                return transformation;
            } catch (final ParsingException e) {
                if (token != null && e instanceof ParsingExceptionImpl) {
                    final ParsingExceptionImpl impl = (ParsingExceptionImpl) e;
                    if (impl.tokens().length == 0) {
                        impl.tokens(new Token[] { token });
                    }
                }
                debug.accept("Could not match node '");
                debug.accept(name);
                debug.accept("' - ");
                debug.accept(e.getMessage());
                debug.accept("\n");
                return null;
            }
        };
    } else {
        transformationFactory = (name, args, token) -> {
            try {
                return combinedResolver.resolve(name, new ArgumentQueueImpl<>(context, args), context);
            } catch (final ParsingException ignored) {
                return null;
            }
        };
    }
    final Predicate<String> tagNameChecker = name -> {
        final String sanitized = TokenParser.TagProvider.sanitizePlaceholderName(name);
        return combinedResolver.has(sanitized);
    };
    final String preProcessed = TokenParser.resolvePreProcessTags(richMessage, transformationFactory);
    context.message(preProcessed);
    // Then, once MiniMessage placeholders have been inserted, we can do the real parse
    final RootNode root = TokenParser.parse(transformationFactory, tagNameChecker, preProcessed, richMessage, context.strict());
    if (debug != null) {
        debug.accept("Text parsed into element tree:\n");
        debug.accept(root.toString());
    }
    return root;
}
Also used : Tag(net.kyori.adventure.text.minimessage.tag.Tag) ElementNode(net.kyori.adventure.text.minimessage.internal.parser.node.ElementNode) Token(net.kyori.adventure.text.minimessage.internal.parser.Token) RootNode(net.kyori.adventure.text.minimessage.internal.parser.node.RootNode) Predicate(java.util.function.Predicate) TagNode(net.kyori.adventure.text.minimessage.internal.parser.node.TagNode) Collectors(java.util.stream.Collectors) ParsingExceptionImpl(net.kyori.adventure.text.minimessage.internal.parser.ParsingExceptionImpl) ArrayList(java.util.ArrayList) Objects(java.util.Objects) Consumer(java.util.function.Consumer) Examinable(net.kyori.examination.Examinable) Nullable(org.jetbrains.annotations.Nullable) List(java.util.List) TokenParser(net.kyori.adventure.text.minimessage.internal.parser.TokenParser) Component(net.kyori.adventure.text.Component) MultiLineStringExaminer(net.kyori.examination.string.MultiLineStringExaminer) BiConsumer(java.util.function.BiConsumer) TokenType(net.kyori.adventure.text.minimessage.internal.parser.TokenType) ValueNode(net.kyori.adventure.text.minimessage.internal.parser.node.ValueNode) NotNull(org.jetbrains.annotations.NotNull) Modifying(net.kyori.adventure.text.minimessage.tag.Modifying) Inserting(net.kyori.adventure.text.minimessage.tag.Inserting) TagResolver(net.kyori.adventure.text.minimessage.tag.resolver.TagResolver) RootNode(net.kyori.adventure.text.minimessage.internal.parser.node.RootNode) TagResolver(net.kyori.adventure.text.minimessage.tag.resolver.TagResolver) TokenParser(net.kyori.adventure.text.minimessage.internal.parser.TokenParser) Token(net.kyori.adventure.text.minimessage.internal.parser.Token) Tag(net.kyori.adventure.text.minimessage.tag.Tag) ParsingExceptionImpl(net.kyori.adventure.text.minimessage.internal.parser.ParsingExceptionImpl) Nullable(org.jetbrains.annotations.Nullable) Examinable(net.kyori.examination.Examinable) NotNull(org.jetbrains.annotations.NotNull)

Aggregations

ArrayList (java.util.ArrayList)3 ElementNode (net.kyori.adventure.text.minimessage.internal.parser.node.ElementNode)3 RootNode (net.kyori.adventure.text.minimessage.internal.parser.node.RootNode)3 TagNode (net.kyori.adventure.text.minimessage.internal.parser.node.TagNode)3 Inserting (net.kyori.adventure.text.minimessage.tag.Inserting)3 Tag (net.kyori.adventure.text.minimessage.tag.Tag)3 List (java.util.List)2 Objects (java.util.Objects)2 BiConsumer (java.util.function.BiConsumer)2 Consumer (java.util.function.Consumer)2 Predicate (java.util.function.Predicate)2 Collectors (java.util.stream.Collectors)2 Component (net.kyori.adventure.text.Component)2 ParsingExceptionImpl (net.kyori.adventure.text.minimessage.internal.parser.ParsingExceptionImpl)2 Token (net.kyori.adventure.text.minimessage.internal.parser.Token)2 TokenParser (net.kyori.adventure.text.minimessage.internal.parser.TokenParser)2 TokenType (net.kyori.adventure.text.minimessage.internal.parser.TokenType)2 ValueNode (net.kyori.adventure.text.minimessage.internal.parser.node.ValueNode)2 Modifying (net.kyori.adventure.text.minimessage.tag.Modifying)2 TagResolver (net.kyori.adventure.text.minimessage.tag.resolver.TagResolver)2