FXMisc / RichTextFX

Rich-text area for JavaFX
BSD 2-Clause "Simplified" License
1.21k stars 237 forks source link

Question: xml highlighter gives StackOverflowError #1212

Closed PavelTurk closed 10 months ago

PavelTurk commented 10 months ago

This is my highlighter:

public class XmlHighlighter {

    private static final Pattern XML_TAG = Pattern.compile("(?<ELEMENT>(</?\\h*)(\\w+)([^<>]*)(\\h*/?>))"
            +"|(?<COMMENT><!--(.|\\v)+?-->)");

    private static final Pattern ATTRIBUTES = Pattern.compile("(\\w+\\h*)(=)(\\h*\"[^\"]+\")");

    private static final int GROUP_OPEN_BRACKET = 2;

    private static final int GROUP_ELEMENT_NAME = 3;

    private static final int GROUP_ATTRIBUTES_SECTION = 4;

    private static final int GROUP_CLOSE_BRACKET = 5;

    private static final int GROUP_ATTRIBUTE_NAME = 1;

    private static final int GROUP_EQUAL_SYMBOL = 2;

    private static final int GROUP_ATTRIBUTE_VALUE = 3;

    public StyleSpans<Collection<String>> computeHighlighting(String text) {
        Matcher matcher = XML_TAG.matcher(text);
        int lastKwEnd = 0;
        StyleSpansBuilder<Collection<String>> spansBuilder = new StyleSpansBuilder<>();
        while(matcher.find()) {
            spansBuilder.add(Collections.emptyList(), matcher.start() - lastKwEnd);
            if(matcher.group("COMMENT") != null) {
                    spansBuilder.add(Collections.singleton("comment"), matcher.end() - matcher.start());
            } else {
                if(matcher.group("ELEMENT") != null) {
                    String attributesText = matcher.group(GROUP_ATTRIBUTES_SECTION);
                    spansBuilder.add(Collections.singleton("tagmark"),
                            matcher.end(GROUP_OPEN_BRACKET) - matcher.start(GROUP_OPEN_BRACKET));
                    spansBuilder.add(Collections.singleton("anytag"),
                            matcher.end(GROUP_ELEMENT_NAME) - matcher.end(GROUP_OPEN_BRACKET));
                    if(!attributesText.isEmpty()) {
                        lastKwEnd = 0;
                        Matcher amatcher = ATTRIBUTES.matcher(attributesText);
                        while(amatcher.find()) {
                            spansBuilder.add(Collections.emptyList(), amatcher.start() - lastKwEnd);
                            spansBuilder.add(Collections.singleton("attribute"),
                                    amatcher.end(GROUP_ATTRIBUTE_NAME) - amatcher.start(GROUP_ATTRIBUTE_NAME));
                            spansBuilder.add(Collections.singleton("tagmark"),
                                    amatcher.end(GROUP_EQUAL_SYMBOL) - amatcher.end(GROUP_ATTRIBUTE_NAME));
                            spansBuilder.add(Collections.singleton("avalue"),
                                    amatcher.end(GROUP_ATTRIBUTE_VALUE) - amatcher.end(GROUP_EQUAL_SYMBOL));
                            lastKwEnd = amatcher.end();
                        }
                        if(attributesText.length() > lastKwEnd) {
                            spansBuilder.add(Collections.emptyList(), attributesText.length() - lastKwEnd);
                        }
                    }
                    lastKwEnd = matcher.end(GROUP_ATTRIBUTES_SECTION);
                    spansBuilder.add(Collections.singleton("tagmark"), matcher.end(GROUP_CLOSE_BRACKET) - lastKwEnd);
                }
            }
            lastKwEnd = matcher.end();
        }
        spansBuilder.add(Collections.emptyList(), text.length() - lastKwEnd);
        return spansBuilder.create();
    }

}

This is xml code that breaks my highlighter:

<!--            <dependency>
                <groupId>org.apache.shiro</groupId>
                <artifactId>shiro-all</artifactId>
                <version>1.11.0</version>
                <scope>compile</scope>
                <exclusions>
                    <exclusion>
                        <groupId>com.google.code.findbugs</groupId>
                        <artifactId>jsr305</artifactId>
                    </exclusion>
                    <exclusion>
                        <groupId>org.apache.shiro</groupId>
                        <artifactId>shiro-config-core</artifactId>
                    </exclusion>
                    <exclusion>
                        <groupId>org.apache.shiro</groupId>
                        <artifactId>shiro-aspectj</artifactId>
                    </exclusion>
                    <exclusion>
                        <groupId>org.apache.shiro</groupId>
                        <artifactId>shiro-cache</artifactId>
                    </exclusion>
                    <exclusion>
                        <groupId>org.apache.shiro</groupId>
                        <artifactId>shiro-core</artifactId>
                    </exclusion>
                    <exclusion>
                        <groupId>org.apache.shiro</groupId>
                        <artifactId>shiro-crypto-cipher</artifactId>
                    </exclusion>
                    <exclusion>
                        <groupId>org.apache.shiro</groupId>
                        <artifactId>shiro-crypto-core</artifactId>
                    </exclusion>
                    <exclusion>
                        <groupId>org.apache.shiro</groupId>
                        <artifactId>shiro-crypto-hash</artifactId>
                    </exclusion>
                    <exclusion>
                        <groupId>org.apache.shiro</groupId>
                        <artifactId>shiro-ehcache</artifactId>
                    </exclusion>
                    <exclusion>
                        <groupId>org.apache.shiro</groupId>
                        <artifactId>shiro-event</artifactId>
                    </exclusion>
                    <exclusion>
                        <groupId>org.apache.shiro</groupId>
                        <artifactId>shiro-quartz</artifactId>
                    </exclusion>
                    <exclusion>
                        <groupId>org.apache.shiro</groupId>
                        <artifactId>shiro-spring</artifactId>
                    </exclusion>
                    <exclusion>
                        <groupId>org.apache.shiro</groupId>
                        <artifactId>shiro-lang</artifactId>
                    </exclusion>
                    <exclusion>
                        <groupId>org.apache.shiro</groupId>
                        <artifactId>shiro-hazelcast</artifactId>
                    </exclusion>
                    <exclusion>
                        <groupId>org.apache.shiro</groupId>
                        <artifactId>shiro-guice</artifactId>
                    </exclusion>
                    <exclusion>
                        <groupId>org.apache.shiro</groupId>
                        <artifactId>shiro-web</artifactId>
                    </exclusion>
                    <exclusion>
                        <groupId>org.apache.shiro</groupId>
                        <artifactId>shiro-config-ogdl</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
             SHIRO ALL doesn't include dependencies, so we add them manually 
            <dependency>
                <groupId>org.apache.shiro</groupId>
                <artifactId>shiro-jaxrs</artifactId>
                <version>1.11.0</version>
                <scope>compile</scope>
                <exclusions>
                    <exclusion>
                        <groupId>com.google.code.findbugs</groupId>
                        <artifactId>jsr305</artifactId>
                    </exclusion>
                    <exclusion>
                        <groupId>org.apache.shiro</groupId>
                        <artifactId>shiro-config-core</artifactId>
                    </exclusion>
                    <exclusion>
                        <groupId>org.apache.shiro</groupId>
                        <artifactId>shiro-aspectj</artifactId>
                    </exclusion>
                    <exclusion>
                        <groupId>org.apache.shiro</groupId>
                        <artifactId>shiro-cache</artifactId>
                    </exclusion>
                    <exclusion>
                        <groupId>org.apache.shiro</groupId>
                        <artifactId>shiro-core</artifactId>
                    </exclusion>
                    <exclusion>
                        <groupId>org.apache.shiro</groupId>
                        <artifactId>shiro-crypto-cipher</artifactId>
                    </exclusion>
                    <exclusion>
                        <groupId>org.apache.shiro</groupId>
                        <artifactId>shiro-crypto-core</artifactId>
                    </exclusion>
                    <exclusion>
                        <groupId>org.apache.shiro</groupId>
                        <artifactId>shiro-crypto-hash</artifactId>
                    </exclusion>
                    <exclusion>
                        <groupId>org.apache.shiro</groupId>
                        <artifactId>shiro-ehcache</artifactId>
                    </exclusion>
                    <exclusion>
                        <groupId>org.apache.shiro</groupId>
                        <artifactId>shiro-event</artifactId>
                    </exclusion>
                    <exclusion>
                        <groupId>org.apache.shiro</groupId>
                        <artifactId>shiro-quartz</artifactId>
                    </exclusion>
                    <exclusion>
                        <groupId>org.apache.shiro</groupId>
                        <artifactId>shiro-spring</artifactId>
                    </exclusion>
                    <exclusion>
                        <groupId>org.apache.shiro</groupId>
                        <artifactId>shiro-lang</artifactId>
                    </exclusion>
                    <exclusion>
                        <groupId>org.apache.shiro</groupId>
                        <artifactId>shiro-hazelcast</artifactId>
                    </exclusion>
                    <exclusion>
                        <groupId>org.apache.shiro</groupId>
                        <artifactId>shiro-guice</artifactId>
                    </exclusion>
                    <exclusion>
                        <groupId>org.apache.shiro</groupId>
                        <artifactId>shiro-web</artifactId>
                    </exclusion>
                    <exclusion>
                        <groupId>org.apache.shiro</groupId>
                        <artifactId>shiro-config-ogdl</artifactId>
                    </exclusion>
                    <exclusion>
                        <groupId>javax.ws.rs</groupId>
                        <artifactId>javax.ws.rs-api</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
            <dependency>
                <groupId>commons-beanutils</groupId>
                <artifactId>commons-beanutils</artifactId>
                <version>1.9.4</version>
            </dependency>-->

And this is what I have:

2023-11-13 23:53:24.310 [ERROR] [JavaFX Application Thread] pk.kit.fx.FxPlatform - Error in FxApplication java.lang.StackOverflowError
    at java.base/java.util.regex.Pattern$Branch.match(Pattern.java:4914)
    at java.base/java.util.regex.Pattern$GroupHead.match(Pattern.java:4969)
    at java.base/java.util.regex.Pattern$LazyLoop.match(Pattern.java:5146)
    at java.base/java.util.regex.Pattern$GroupTail.match(Pattern.java:5000)
    at java.base/java.util.regex.Pattern$BranchConn.match(Pattern.java:4878)
    at java.base/java.util.regex.Pattern$CharProperty.match(Pattern.java:4110)
    at java.base/java.util.regex.Pattern$Branch.match(Pattern.java:4914)
    at java.base/java.util.regex.Pattern$GroupHead.match(Pattern.java:4969)
    at java.base/java.util.regex.Pattern$LazyLoop.match(Pattern.java:5146)
    at java.base/java.util.regex.Pattern$GroupTail.match(Pattern.java:5000)
    at java.base/java.util.regex.Pattern$BranchConn.match(Pattern.java:4878)
    at java.base/java.util.regex.Pattern$CharProperty.match(Pattern.java:4110)
    at java.base/java.util.regex.Pattern$Branch.match(Pattern.java:4914)
    at java.base/java.util.regex.Pattern$GroupHead.match(Pattern.java:4969)
    at java.base/java.util.regex.Pattern$LazyLoop.match(Pattern.java:5146)
    at java.base/java.util.regex.Pattern$GroupTail.match(Pattern.java:5000)
    at java.base/java.util.regex.Pattern$BranchConn.match(Pattern.java:4878)
    at java.base/java.util.regex.Pattern$CharProperty.match(Pattern.java:4110)
    at java.base/java.util.regex.Pattern$Branch.match(Pattern.java:4914)
    at java.base/java.util.regex.Pattern$GroupHead.match(Pattern.java:4969)
    at java.base/java.util.regex.Pattern$LazyLoop.match(Pattern.java:5146)

Could anyone say how to fix it?

Jugen commented 10 months ago

If you haven't done so already, I think you're likely to get a helpful response posting on a forum specializing in Java regex.

PavelTurk commented 10 months ago

I found solution here - https://superuser.com/a/1153242/491751. So, final pattern that worked for me is

 private static final Pattern XML_TAG = Pattern.compile("(?<ELEMENT>(</?\\h*)(\\w+)([^<>]*)(\\h*/?>))"
                +"|(?<COMMENT><!--[\\s\\S\\n]*?-->)");