Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 0 additions & 13 deletions exist-core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -390,19 +390,6 @@
<artifactId>Saxon-HE</artifactId>
</dependency>

<dependency>
<groupId>org.exist-db</groupId>
<artifactId>exist-saxon-regex</artifactId>
<version>9.4.0-9.e1</version>
<exclusions>
<exclusion>
<!-- we have our own dependency on Saxon -->
<groupId>net.sf.saxon</groupId>
<artifactId>Saxon-HE</artifactId>
</exclusion>
</exclusions>
</dependency>

<dependency>
<groupId>com.evolvedbinary.thirdparty.org.apache.xmlrpc</groupId>
<artifactId>xmlrpc-common</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -267,14 +267,16 @@ throws PermissionDeniedException, EXistException, XPathException
v:VERSION_DECL
{
final String version = v.getText();
if (version.equals("3.1")) {
if (version.equals("4.0")) {
context.setXQueryVersion(40);
} else if (version.equals("3.1")) {
context.setXQueryVersion(31);
} else if (version.equals("3.0")) {
context.setXQueryVersion(30);
} else if (version.equals("1.0")) {
context.setXQueryVersion(10);
} else {
throw new XPathException(v, ErrorCodes.XQST0031, "Wrong XQuery version: require 1.0, 3.0 or 3.1");
throw new XPathException(v, ErrorCodes.XQST0031, "Wrong XQuery version: require 1.0, 3.0, 3.1, or 4.0");
}
}
( enc:STRING_LITERAL )?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -822,6 +822,7 @@ public Attr getAttributeNodeNS(final String namespaceURI, final String localName
@Override
public NamedNodeMap getAttributes() {
final org.exist.dom.NamedNodeMapImpl map = new NamedNodeMapImpl(ownerDocument, true);

if(hasAttributes()) {
try(final DBBroker broker = ownerDocument.getBrokerPool().getBroker();
final INodeIterator iterator = broker.getNodeIterator(this)) {
Expand All @@ -837,6 +838,14 @@ public NamedNodeMap getAttributes() {
if(next.getNodeType() != Node.ATTRIBUTE_NODE) {
break;
}
// Skip namespace declarations for the XML namespace — the xml prefix
// is always implicitly bound and Saxon 12 rejects any explicit
// declaration involving http://www.w3.org/XML/1998/namespace
if (next.getNodeType() == Node.ATTRIBUTE_NODE
&& Namespaces.XMLNS_NS.equals(next.getNamespaceURI())
&& XMLConstants.XML_NS_URI.equals(next.getNodeValue())) {
continue;
}
map.setNamedItem(next);
}
} catch(final EXistException | IOException e) {
Expand All @@ -847,6 +856,13 @@ public NamedNodeMap getAttributes() {
for (final Map.Entry<String, String> entry : namespaceMappings.entrySet()) {
final String prefix = entry.getKey();
final String ns = entry.getValue();
// Skip namespace declarations involving the XML namespace URI —
// Saxon 12 rejects any explicit declaration of the xml prefix
// or binding of the XML namespace to a non-xml prefix
if (XMLConstants.XML_NS_PREFIX.equals(prefix)
|| XMLConstants.XML_NS_URI.equals(ns)) {
continue;
}
final QName attrName = new QName(prefix, Namespaces.XMLNS_NS, XMLConstants.XMLNS_ATTRIBUTE);
final AttrImpl attr = new AttrImpl(getExpression(), attrName, ns, null);
attr.setOwnerDocument(ownerDocument);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,8 @@
import org.exist.security.PermissionDeniedException;
import org.exist.storage.DBBroker;
import org.exist.storage.lock.Lock.LockMode;
import org.exist.thirdparty.net.sf.saxon.functions.regex.JDK15RegexTranslator;
import org.exist.thirdparty.net.sf.saxon.functions.regex.RegexSyntaxException;
import org.exist.thirdparty.net.sf.saxon.functions.regex.RegularExpression;
import net.sf.saxon.regex.JavaRegularExpression;
import net.sf.saxon.str.StringView;
import org.exist.util.XMLReaderPool;
import org.exist.xmldb.XmldbURI;
import org.exist.xquery.Constants;
Expand Down Expand Up @@ -272,16 +271,13 @@ private static final class Mapping {

private Mapping(String regex, final URLRewrite action) throws ServletException {
try {
final int options = RegularExpression.XML11 | RegularExpression.XPATH30;
int flagbits = 0;

final List<RegexSyntaxException> warnings = new ArrayList<>();
regex = JDK15RegexTranslator.translate(regex, options, flagbits, warnings);
final JavaRegularExpression javaRegex = new JavaRegularExpression(StringView.of(regex), "");
regex = javaRegex.getJavaRegularExpression();

this.pattern = Pattern.compile(regex, 0);
this.action = action;
this.matcher = pattern.matcher("");
} catch (final RegexSyntaxException e) {
} catch (final net.sf.saxon.trans.XPathException e) {
throw new ServletException("Syntax error in regular expression specified for path. " +
e.getMessage(), e);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -825,7 +825,8 @@ public void setStylesheet(final Document doc, final @Nullable String stylesheet)

// restore handlers
receiver = oldReceiver;
factory.get().setURIResolver(null);
// Saxon 12 rejects null URIResolver; reset to default identity resolver
factory.get().setURIResolver((href, base) -> null);
}
LOG.debug("compiling stylesheet took {}", System.currentTimeMillis() - start);
if (templates != null) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
/*
* eXist-db Open Source Native XML Database
* Copyright (C) 2001 The eXist-db Authors
*
* info@exist-db.org
* http://www.exist-db.org
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package org.exist.util;

import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;

/**
* A SAX ContentHandler wrapper that suppresses duplicate startDocument/endDocument calls.
* Saxon 12's LinkedTreeBuilder does not tolerate receiving startDocument more than once,
* which can happen when eXist's Serializer sends document events that overlap with
* explicitly-called startDocument/endDocument in the XSLT compilation pipeline.
*/
public class XMLBackwardsCompatHandler implements ContentHandler {

private final ContentHandler delegate;
private boolean documentStarted = false;

public XMLBackwardsCompatHandler(final ContentHandler delegate) {
this.delegate = delegate;
}

@Override
public void startDocument() throws SAXException {
if (!documentStarted) {
documentStarted = true;
delegate.startDocument();
}
}

@Override
public void endDocument() throws SAXException {
// Suppress — the caller will call endDocument on the delegate directly
}

@Override
public void setDocumentLocator(final Locator locator) {
delegate.setDocumentLocator(locator);
}

@Override
public void startPrefixMapping(final String prefix, final String uri) throws SAXException {
// Saxon 12 rejects any namespace declaration involving the XML namespace URI
// (http://www.w3.org/XML/1998/namespace) — the xml prefix is always implicitly bound
if ("xml".equals(prefix) || javax.xml.XMLConstants.XML_NS_URI.equals(uri)) {
return;
}
delegate.startPrefixMapping(prefix, uri);
}

@Override
public void endPrefixMapping(final String prefix) throws SAXException {
delegate.endPrefixMapping(prefix);
}

@Override
public void startElement(final String uri, final String localName, final String qName, final Attributes atts) throws SAXException {
delegate.startElement(uri, localName, qName, atts);
}

@Override
public void endElement(final String uri, final String localName, final String qName) throws SAXException {
delegate.endElement(uri, localName, qName);
}

@Override
public void characters(final char[] ch, final int start, final int length) throws SAXException {
delegate.characters(ch, start, length);
}

@Override
public void ignorableWhitespace(final char[] ch, final int start, final int length) throws SAXException {
delegate.ignorableWhitespace(ch, start, length);
}

@Override
public void processingInstruction(final String target, final String data) throws SAXException {
delegate.processingInstruction(target, data);
}

@Override
public void skippedEntity(final String name) throws SAXException {
delegate.skippedEntity(name);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ public class XmlLibraryChecker {
* Possible XML Transformers, at least one must be valid
*/
private final static ClassVersion[] validTransformers = {
new ClassVersion("Saxon", "8.9.0", "net.sf.saxon.Version.getProductVersion()"),
new ClassVersion("Saxon", "12.0", "net.sf.saxon.Version.getProductVersion()"),
new ClassVersion("Xalan", "Xalan Java 2.7.1", "org.apache.xalan.Version.getVersion()"),
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,11 @@
import java.util.List;

import net.sf.saxon.Configuration;
import net.sf.saxon.om.Item;
import net.sf.saxon.regex.RegexIterator;
import net.sf.saxon.regex.RegexMatchHandler;
import net.sf.saxon.regex.RegularExpression;
import net.sf.saxon.str.StringView;
import net.sf.saxon.str.UnicodeString;
import org.exist.dom.QName;
import org.exist.dom.memtree.MemTreeBuilder;
import org.exist.xquery.*;
Expand Down Expand Up @@ -126,15 +128,15 @@ private void analyzeString(final MemTreeBuilder builder, final String input, Str
final List<String> warnings = new ArrayList<>(1);

try {
final RegularExpression regularExpression = config.compileRegularExpression(pattern, flags, "XP30", warnings);
if (regularExpression.matches("")) {
final RegularExpression regularExpression = config.compileRegularExpression(StringView.of(pattern), flags, "XP30", warnings);
if (regularExpression.matches(StringView.of(""))) {
throw new XPathException(this, ErrorCodes.FORX0003, "regular expression could match empty string");
}

//TODO(AR) cache the regular expression... might be possible through Saxon config

final RegexIterator regexIterator = regularExpression.analyze(input);
Item item;
final RegexIterator regexIterator = regularExpression.analyze(StringView.of(input));
net.sf.saxon.value.StringValue item;
while ((item = regexIterator.next()) != null) {
if (regexIterator.isMatching()) {
match(builder, regexIterator);
Expand All @@ -147,7 +149,7 @@ private void analyzeString(final MemTreeBuilder builder, final String input, Str
LOG.warn(warning);
}
} catch (final net.sf.saxon.trans.XPathException e) {
switch (e.getErrorCodeLocalPart()) {
switch (e.getErrorCodeQName().getLocalPart()) {
case "FORX0001" -> throw new XPathException(this, ErrorCodes.FORX0001, e.getMessage());
case "FORX0002" -> throw new XPathException(this, ErrorCodes.FORX0002, e.getMessage());
case "FORX0003" -> throw new XPathException(this, ErrorCodes.FORX0003, e.getMessage());
Expand All @@ -158,10 +160,10 @@ private void analyzeString(final MemTreeBuilder builder, final String input, Str

private void match(final MemTreeBuilder builder, final RegexIterator regexIterator) throws net.sf.saxon.trans.XPathException {
builder.startElement(QN_MATCH, null);
regexIterator.processMatchingSubstring(new RegexIterator.MatchHandler() {
regexIterator.processMatchingSubstring(new RegexMatchHandler() {
@Override
public void characters(final CharSequence s) {
builder.characters(s);
public void characters(final UnicodeString s) {
builder.characters(s.toString());
}

@Override
Expand All @@ -180,9 +182,9 @@ public void onGroupEnd(final int groupNumber) throws net.sf.saxon.trans.XPathExc
builder.endElement();
}

private void nonMatch(final MemTreeBuilder builder, final Item item) {
private void nonMatch(final MemTreeBuilder builder, final net.sf.saxon.value.StringValue item) {
builder.startElement(QN_NON_MATCH, null);
builder.characters(item.getStringValueCS());
builder.characters(item.getStringValue());
builder.endElement();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
import net.sf.saxon.regex.RegularExpression;
import net.sf.saxon.str.StringView;

import static org.exist.xquery.FunctionDSL.*;
import static org.exist.xquery.functions.fn.FnModule.functionSignatures;
Expand Down Expand Up @@ -517,16 +518,16 @@ private boolean matchXmlRegex(final String string, final String pattern, final S
List<String> warnings = new ArrayList<>(1);
RegularExpression regex = context.getBroker().getBrokerPool()
.getSaxonConfiguration()
.compileRegularExpression(pattern, flags, "XP30", warnings);
.compileRegularExpression(StringView.of(pattern), flags, "XP30", warnings);

for (final String warning : warnings) {
LOG.warn(warning);
}

return regex.containsMatch(string);
return regex.containsMatch(StringView.of(string));

} catch (final net.sf.saxon.trans.XPathException e) {
switch (e.getErrorCodeLocalPart()) {
switch (e.getErrorCodeQName().getLocalPart()) {
case "FORX0001" -> throw new XPathException(this, ErrorCodes.FORX0001, "Invalid regular expression: " + e.getMessage());
case "FORX0002" -> throw new XPathException(this, ErrorCodes.FORX0002, "Invalid regular expression: " + e.getMessage());
// no FORX0003 here since fn:matches is allowed to match an empty string
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import net.sf.saxon.Configuration;
import net.sf.saxon.functions.Replace;
import net.sf.saxon.regex.RegularExpression;
import net.sf.saxon.str.StringView;
import org.exist.dom.QName;
import org.exist.xquery.*;
import org.exist.xquery.value.FunctionParameterSequenceType;
Expand Down Expand Up @@ -119,24 +120,28 @@ public Sequence eval(final Sequence[] args, final Sequence contextSequence) thro
final List<String> warnings = new ArrayList<>(1);

try {
final RegularExpression regularExpression = config.compileRegularExpression(pattern, flags, "XP30", warnings);
if (regularExpression.matches("")) {
final RegularExpression regularExpression = config.compileRegularExpression(StringView.of(pattern), flags, "XP30", warnings);
final boolean canMatchEmpty = regularExpression.matches(StringView.of(""));

// XQ 3.1: FORX0003 if regex can match empty string
// XQ 4.0: empty-matching regex is allowed
if (canMatchEmpty && context.getXQueryVersion() < 40) {
throw new XPathException(this, ErrorCodes.FORX0003, "regular expression could match empty string");
}

//TODO(AR) cache the regular expression... might be possible through Saxon config

if (!hasLiteral(flags)) {
final String msg = Replace.checkReplacement(replace);
final String msg = Replace.checkReplacement(StringView.of(replace));
if (msg != null) {
throw new XPathException(this, ErrorCodes.FORX0004, msg);
}
}
final CharSequence res = regularExpression.replace(string, replace);
final net.sf.saxon.str.UnicodeString res = regularExpression.replace(StringView.of(string), StringView.of(replace));
result = new StringValue(this, res.toString());

} catch (final net.sf.saxon.trans.XPathException e) {
switch (e.getErrorCodeLocalPart()) {
switch (e.getErrorCodeQName().getLocalPart()) {
case "FORX0001" -> throw new XPathException(this, ErrorCodes.FORX0001, e.getMessage());
case "FORX0002" -> throw new XPathException(this, ErrorCodes.FORX0002, e.getMessage());
case "FORX0003" -> throw new XPathException(this, ErrorCodes.FORX0003, e.getMessage());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,11 @@
try {
final Pattern pat = PatternFactory.getInstance().getPattern(pattern, flags);
if (pat.matcher("").matches()) {
throw new XPathException(this, ErrorCodes.FORX0003, "regular expression could match empty string");
// XQ 3.1: FORX0003 if regex can match empty string
// XQ 4.0: empty-matching regex is allowed
if (context.getXQueryVersion() < 40) {

Check notice on line 112 in exist-core/src/main/java/org/exist/xquery/functions/fn/FunTokenize.java

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

exist-core/src/main/java/org/exist/xquery/functions/fn/FunTokenize.java#L112

These nested if statements could be combined
throw new XPathException(this, ErrorCodes.FORX0003, "regular expression could match empty string");
}
}

final String[] tokens = pat.split(string, -1);
Expand Down
Loading
Loading