javaregexxmlxsdxerces

RegEx in XML Schema .xsd


I have an xsd schema file which uses a pattern which is basically RegEx but I have problems creating the correct RegEx. My RegEx should accept these:

I got to this RegEx: ^(@\{.*\.(png|jpg)\}|([A-Z]:)?/.*\.(png|jpg))$ which works using the Java ReGex matcher and on regex101 but the xml parser throws an exception and says that it doesn't match.

I also found out that the xml regex matching engine is using Non-deterministic finite automaton but I dont know what that means for my RegEx:

A regular expression matching engine using Non-deterministic Finite Automaton (NFA). This engine does not conform to the POSIX regular expression.

valid.xml

<?xml version="1.0" encoding="UTF-8"?>
<frameui xmlns="http://www.squidxtv.me/schema/frameui/v1/frameui.xsd"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://www.squidxtv.me/schema/frameui/v1 /frameui.xsd"
         id="valid-frame"
         width="5"
         height="3"
         background-image="@{../test-image.png}">
    <div id="first-div" width="500" height="500" x="100" y="100" border-color="#FFFFFF" border-width="1">
        <text id="first-test" x="100" y="100" underline="true" border-color="#FFFFFF" border-width="1">Test Text</text>
    </div>
</frameui>

frameui.xsd

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
           elementFormDefault="qualified"
           targetNamespace="http://www.squidxtv.me/schema/frameui/v1/frameui.xsd"
           xmlns="http://www.squidxtv.me/schema/frameui/v1/frameui.xsd"
           version="1.0">
    <xs:element name="frameui">
        <xs:complexType>
            <xs:all>
                <xs:element name="text" type="TextType" minOccurs="0"/>
                <xs:element name="image" type="ImgType" minOccurs="0"/>
                <xs:element name="div" type="DivType" minOccurs="0"/>
            </xs:all>
            <xs:attribute name="id" type="xs:string" use="required"/>
            <xs:attribute name="width" type="xs:positiveInteger" use="required"/>
            <xs:attribute name="height" type="xs:positiveInteger" use="required"/>
            <xs:attribute name="background-color" type="hexType" default="#0D0D0D"/>
            <xs:attribute name="background-image" type="pathType"/>
            <xs:attributeGroup ref="border"/>
            <xs:attribute name="click-radius" type="xs:positiveInteger" default="6"/>
            <xs:attribute name="scroll-radius" type="xs:positiveInteger" default="6"/>
        </xs:complexType>
    </xs:element>

    <xs:complexType name="TextType">
        <xs:simpleContent>
            <xs:extension base="xs:string">
                <xs:attribute name="id" type="xs:string" use="required"/>
                <xs:attributeGroup ref="position"/>
                <xs:attributeGroup ref="font"/>
                <xs:attributeGroup ref="border"/>
            </xs:extension>
        </xs:simpleContent>
    </xs:complexType>

    <xs:complexType name="ImgType">
        <xs:attribute name="id" type="xs:string" use="required"/>
        <xs:attributeGroup ref="position"/>
        <xs:attribute name="path" type="pathType" use="required"/>
        <xs:attributeGroup ref="border"/>
    </xs:complexType>

    <xs:complexType name="DivType">
        <xs:all>
            <xs:element name="text" type="TextType" minOccurs="0"/>
            <xs:element name="image" type="ImgType" minOccurs="0"/>
            <xs:element name="div" type="DivType" minOccurs="0"/>
        </xs:all>
        <xs:attribute name="id" type="xs:string" use="required"/>
        <xs:attributeGroup ref="position"/>
        <xs:attribute name="width" type="xs:positiveInteger" use="required"/>
        <xs:attribute name="height" type="xs:positiveInteger" use="required"/>
        <xs:attributeGroup ref="border"/>
    </xs:complexType>

    <xs:attributeGroup name="font">
        <xs:attribute name="font" type="xs:string" default="Sans Serif"/>
        <xs:attribute name="font-size" type="xs:positiveInteger" default="12"/>
        <xs:attribute name="font-color" type="hexType" default="#FFFFFF"/>
        <xs:attribute name="bold" type="xs:boolean" default="false"/>
        <xs:attribute name="italic" type="xs:boolean" default="false"/>
        <xs:attribute name="underline" type="xs:boolean" default="false"/>
    </xs:attributeGroup>

    <xs:attributeGroup name="border">
        <xs:attribute name="border-color" type="hexType" default="#0D0D0D"/>
        <xs:attribute name="border-width" type="xs:nonNegativeInteger" default="0"/>
    </xs:attributeGroup>

    <xs:attributeGroup name="position">
        <xs:attribute name="x" type="xs:integer" default="0"/>
        <xs:attribute name="y" type="xs:integer" default="0"/>
    </xs:attributeGroup>

    <xs:simpleType name="hexType">
        <xs:restriction base="xs:string">
            <xs:pattern value="#[0-9a-fA-F]{6}"/>
        </xs:restriction>
    </xs:simpleType>

    <xs:simpleType name="pathType">
        <xs:restriction base="xs:string">
            <xs:pattern value="^(@\{.*\.(png|jpg)\}|([A-Z]:)?/.*\.(png|jpg))$"/>
            <xs:whiteSpace value="collapse"/>
        </xs:restriction>
    </xs:simpleType>

</xs:schema>

test

@Test
public void testValidXml() {
        ScreenType model = assertDoesNotThrow(() -> parser.parse(Path.of(getClass().getResource("/guis/valid.xml").toURI())));
}

parser configuration:

package me.squidxtv.frameui.core.parser;

import me.squidxtv.frameui.core.content.*;
import org.bukkit.plugin.java.JavaPlugin;
import org.jetbrains.annotations.NotNull;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import javax.xml.XMLConstants;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Objects;
import java.util.function.Predicate;
import java.util.regex.Pattern;

/**
 * The ScreenParser class provides methods for parsing an XML file into a ScreenType object
 * and retrieving information about the child nodes of a given Node.
 */
public class ScreenParser {

    /**
     * The regular expression pattern for matching Windows-style paths.
     */
    private static final @NotNull Predicate<String> WINDOWS_PATH = Pattern.compile("^[A-Z]:\\\\.+$").asMatchPredicate();
    /**
     * The regular expression pattern for matching Unix-style paths.
     */
    private static final @NotNull Predicate<String> UNIX_PATH = Pattern.compile("^/.+$").asMatchPredicate();
    /**
     * The regular expression pattern for matching relative paths.
     */
    private static final @NotNull Predicate<String> RELATIVE_PATH = Pattern.compile("^@\\{.+}$").asMatchPredicate();

    /**
     * The {@link DocumentBuilder} used for parsing XML files.
     */
    private final @NotNull DocumentBuilder documentBuilder;

    /**
     * Constructs a new instance of {@link ScreenParser} class.
     * @param plugin
     * @throws SAXException if there is an error in the XML schema.
     * @throws ParserConfigurationException if there is a configuration error in the {@link DocumentBuilder}.
     */
    public ScreenParser(JavaPlugin plugin) throws SAXException, ParserConfigurationException {
        SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
        Schema schema = schemaFactory.newSchema(getClass().getResource("/frameui.xsd"));

        DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
        documentBuilderFactory.setSchema(schema);
        documentBuilderFactory.setNamespaceAware(true);
        documentBuilder = documentBuilderFactory.newDocumentBuilder();

        ParserErrorHandler errorHandler = new ParserErrorHandler(plugin);
        documentBuilder.setErrorHandler(errorHandler);
    }

    /**
     * Parses an XML file and returns a ScreenType object representing the root element of the document.
     * @param xmlFile the Path to the XML file to be parsed
     * @return a ScreenType object representing the root element of the parsed document
     * @throws IOException if an I/O error occurs while reading the file
     * @throws SAXException if an error occurs during parsing
     */
    public ScreenType parse(Path xmlFile) throws IOException, SAXException {
        Document xml = documentBuilder.parse(xmlFile.toFile());
        Element root = xml.getDocumentElement();
        return ScreenType.of(root, xmlFile);
    }

    /**
     * Returns the {@link DocumentBuilder} used by this {@link ScreenParser} instance.
     * @return the {@link DocumentBuilder} used by this {@link ScreenParser} instance
     */
    public DocumentBuilder getDocumentBuilder() {
        return documentBuilder;
    }

    /**
     * Returns an array of ComplexType objects representing the child nodes of the specified root Node.
     *
     * @param root the root Node whose children to retrieve
     * @param path the path to the XML file containing the root Node
     * @return an array of ComplexType objects representing the child nodes of the root Node
     */
    public static ComplexType[] getChildren(Node root, Path path) {
        NodeList nodes = root.getChildNodes();
        if (nodes.getLength() == 0) {
            return new ComplexType[0];
        }

        ComplexType[] sub = new ComplexType[nodes.getLength()];
        for (int i = 0; i < nodes.getLength(); i++) {
            Node current = nodes.item(i);
            String name = current.getNodeName();
            if (name.equals("#text")) {
                continue;
            }

            switch (name) {
                case "text" -> sub[i] = TextType.of((Element) current);
                case "image" -> sub[i] = ImageType.of((Element) current, path);
                case "div" -> sub[i] = DivType.of((Element) current, path);
            }
        }
        return Arrays.stream(sub).filter(Objects::nonNull).toArray(ComplexType[]::new);
    }

    /**
     * Converts the given path string to a {@link Path} object.
     *
     * @param original the path string to convert
     * @param xml the path to the XML file used as the base for relative paths
     * @return the {@link Path} object corresponding to the input path string
     * @throws IllegalArgumentException if the input path string is not in a recognized format
     */
    public static Path convert(String original, Path xml) {
        if (RELATIVE_PATH.test(original)) {
            return xml.resolve(original.substring(2, original.length() - 1));
        }

        if (WINDOWS_PATH.test(original) || UNIX_PATH.test(original)) {
            return Paths.get(original);
        }

        throw new IllegalArgumentException("Invalid path format: " + original);
    }

}

exception:

OpenJDK 64-Bit Server VM warning: Sharing is only supported for boot loader classes because bootstrap classpath has been appended

org.opentest4j.AssertionFailedError: Unexpected exception thrown: org.xml.sax.SAXException: org.xml.sax.SAXParseException; systemId: file:/D:/intellij-workspace/FrameUI/target/test-classes/guis/valid.xml; lineNumber: 8; columnNumber: 50; cvc-pattern-valid: Value '@{../test-image.png}' is not facet-valid with respect to pattern '^(@\{.*\.(png|jpg)\}|([A-Z]:)?/.*\.(png|jpg))$' for type 'pathType'.

    at org.junit.jupiter.api.AssertionFailureBuilder.build(AssertionFailureBuilder.java:152)
    at org.junit.jupiter.api.AssertDoesNotThrow.createAssertionFailedError(AssertDoesNotThrow.java:84)
    at org.junit.jupiter.api.AssertDoesNotThrow.assertDoesNotThrow(AssertDoesNotThrow.java:75)
    at org.junit.jupiter.api.AssertDoesNotThrow.assertDoesNotThrow(AssertDoesNotThrow.java:58)
    at org.junit.jupiter.api.Assertions.assertDoesNotThrow(Assertions.java:3196)
    at me.squidxtv.frameui.core.parser.ScreenParserTest.testValidXml(ScreenParserTest.java:53)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:568)
    at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:727)
    at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60)
    at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131)
    at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:156)
    at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:147)
    at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestMethod(TimeoutExtension.java:86)
    at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(InterceptingExecutableInvoker.java:103)
    at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.lambda$invoke$0(InterceptingExecutableInvoker.java:93)
    at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106)
    at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64)
    at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45)
    at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37)
    at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.invoke(InterceptingExecutableInvoker.java:92)
    at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.invoke(InterceptingExecutableInvoker.java:86)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$7(TestMethodTestDescriptor.java:217)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:213)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:138)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:68)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:151)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
    at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
    at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
    at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
    at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
    at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
    at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:35)
    at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
    at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:54)
    at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:147)
    at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:127)
    at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:90)
    at org.junit.platform.launcher.core.EngineExecutionOrchestrator.lambda$execute$0(EngineExecutionOrchestrator.java:55)
    at org.junit.platform.launcher.core.EngineExecutionOrchestrator.withInterceptedStreams(EngineExecutionOrchestrator.java:102)
    at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:54)
    at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:114)
    at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:86)
    at org.junit.platform.launcher.core.DefaultLauncherSession$DelegatingLauncher.execute(DefaultLauncherSession.java:86)
    at org.junit.platform.launcher.core.SessionPerRequestLauncher.execute(SessionPerRequestLauncher.java:53)
    at com.intellij.junit5.JUnit5IdeaTestRunner.startRunnerWithArgs(JUnit5IdeaTestRunner.java:57)
    at com.intellij.rt.junit.IdeaTestRunner$Repeater$1.execute(IdeaTestRunner.java:38)
    at com.intellij.rt.execution.junit.TestsRepeater.repeat(TestsRepeater.java:11)
    at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:35)
    at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:232)
    at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:55)
Caused by: org.xml.sax.SAXException: org.xml.sax.SAXParseException; systemId: file:/D:/intellij-workspace/FrameUI/target/test-classes/guis/valid.xml; lineNumber: 8; columnNumber: 50; cvc-pattern-valid: Value '@{../test-image.png}' is not facet-valid with respect to pattern '^(@\{.*\.(png|jpg)\}|([A-Z]:)?/.*\.(png|jpg))$' for type 'pathType'.
org.xml.sax.SAXParseException; systemId: file:/D:/intellij-workspace/FrameUI/target/test-classes/guis/valid.xml; lineNumber: 8; columnNumber: 50; cvc-pattern-valid: Value '@{../test-image.png}' is not facet-valid with respect to pattern '^(@\{.*\.(png|jpg)\}|([A-Z]:)?/.*\.(png|jpg))$' for type 'pathType'.
    at me.squidxtv.frameui.core.parser.ParserErrorHandler.error(ParserErrorHandler.java:40)
    at java.xml/com.sun.org.apache.xerces.internal.util.ErrorHandlerWrapper.error(ErrorHandlerWrapper.java:138)
    at java.xml/com.sun.org.apache.xerces.internal.impl.XMLErrorReporter.reportError(XMLErrorReporter.java:396)
    at java.xml/com.sun.org.apache.xerces.internal.impl.XMLErrorReporter.reportError(XMLErrorReporter.java:327)
    at java.xml/com.sun.org.apache.xerces.internal.impl.XMLErrorReporter.reportError(XMLErrorReporter.java:284)
    at java.xml/com.sun.org.apache.xerces.internal.impl.xs.XMLSchemaValidator$XSIErrorReporter.reportError(XMLSchemaValidator.java:512)
    at java.xml/com.sun.org.apache.xerces.internal.impl.xs.XMLSchemaValidator.reportSchemaError(XMLSchemaValidator.java:3600)
    at java.xml/com.sun.org.apache.xerces.internal.impl.xs.XMLSchemaValidator.processOneAttribute(XMLSchemaValidator.java:3108)
    at java.xml/com.sun.org.apache.xerces.internal.impl.xs.XMLSchemaValidator.processAttributes(XMLSchemaValidator.java:3052)
    at java.xml/com.sun.org.apache.xerces.internal.impl.xs.XMLSchemaValidator.handleStartElement(XMLSchemaValidator.java:2287)
    at java.xml/com.sun.org.apache.xerces.internal.impl.xs.XMLSchemaValidator.startElement(XMLSchemaValidator.java:830)
    at java.xml/com.sun.org.apache.xerces.internal.impl.XMLNSDocumentScannerImpl.scanStartElement(XMLNSDocumentScannerImpl.java:374)
    at java.xml/com.sun.org.apache.xerces.internal.impl.XMLNSDocumentScannerImpl$NSContentDriver.scanRootElementHook(XMLNSDocumentScannerImpl.java:613)
    at java.xml/com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl$FragmentContentDriver.next(XMLDocumentFragmentScannerImpl.java:3079)
    at java.xml/com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl$PrologDriver.next(XMLDocumentScannerImpl.java:836)
    at java.xml/com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl.next(XMLDocumentScannerImpl.java:605)
    at java.xml/com.sun.org.apache.xerces.internal.impl.XMLNSDocumentScannerImpl.next(XMLNSDocumentScannerImpl.java:112)
    at java.xml/com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanDocument(XMLDocumentFragmentScannerImpl.java:542)
    at java.xml/com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:889)
    at java.xml/com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:825)
    at java.xml/com.sun.org.apache.xerces.internal.parsers.XMLParser.parse(XMLParser.java:141)
    at java.xml/com.sun.org.apache.xerces.internal.parsers.DOMParser.parse(DOMParser.java:247)
    at java.xml/com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderImpl.parse(DocumentBuilderImpl.java:342)
    at java.xml/javax.xml.parsers.DocumentBuilder.parse(DocumentBuilder.java:206)
    at me.squidxtv.frameui.core.parser.ScreenParser.parse(ScreenParser.java:77)
    at me.squidxtv.frameui.core.parser.ScreenParserTest.lambda$testValidXml$1(ScreenParserTest.java:53)
    at org.junit.jupiter.api.AssertDoesNotThrow.assertDoesNotThrow(AssertDoesNotThrow.java:71)
    ... 73 more
Caused by: org.xml.sax.SAXParseException; systemId: file:/D:/intellij-workspace/FrameUI/target/test-classes/guis/valid.xml; lineNumber: 8; columnNumber: 50; cvc-pattern-valid: Value '@{../test-image.png}' is not facet-valid with respect to pattern '^(@\{.*\.(png|jpg)\}|([A-Z]:)?/.*\.(png|jpg))$' for type 'pathType'.
    at java.xml/com.sun.org.apache.xerces.internal.util.ErrorHandlerWrapper.createSAXParseException(ErrorHandlerWrapper.java:204)
    at java.xml/com.sun.org.apache.xerces.internal.util.ErrorHandlerWrapper.error(ErrorHandlerWrapper.java:135)
    ... 98 more


Process finished with exit code -1


Solution

  • Regexp matching looks like a limited implementation for xsd.
    Removing some unneeded grouping and refactoring regex parts to avoid repeating (jpg|png) pattern.

    (@\{.*|[A-Z]:/.*)\.(png|jpg)\}?
    

    Match

    @{../test-image.png}
    Z:/test/image.jpg
    

    A more verbose but simplified pattern closer to w3.org regexp definition.

    @\{.*\.(png|jpg)\}|[A-Z]:/.*\.(png|jpg)
    

    Testing on command line (libxml2 implementation)

    xmllint --schema tmp.xsd --noout tmp.xml 
    tmp.xml validates
    

    Note: got error as described with original regex

    tmp.xml:8: element frameui: Schemas validity error : Element '{http://www.squidxtv.me/schema/frameui/v1/frameui.xsd}frameui', attribute 'background-image': [facet 'pattern'] The value '@{../test-image.png}' is not accepted by the pattern '^(@\{.*\.(png|jpg)\}|([A-Z]:)?/.*\.(png|jpg))$'.