-
Notifications
You must be signed in to change notification settings - Fork 5
Add iXML support #239
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop-7.x.x
Are you sure you want to change the base?
Add iXML support #239
Changes from all commits
1f3c10e
f8481bd
df6b12c
1390a7c
8585ef0
6cb1da6
e72d75c
1be12bb
537a8bd
9fb2203
460dec7
9fde446
e104b6a
e6891e4
bb44afe
5d6eaa3
96ced97
cd1f623
dd6e448
5c4f914
7d9ae4a
9bdc7ac
39bfd54
aa29c5f
17c83e9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -584,6 +584,12 @@ | |
| <!-- scope>test</scope --> <!-- not just test scope, as needed for org.exist.test --> | ||
| </dependency> | ||
|
|
||
| <dependency> | ||
| <groupId>de.bottlecaps</groupId> | ||
| <artifactId>markup-blitz</artifactId> | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Which version of Java does this require? Will it be compatible with Elemental 6.x.x (JDK 8)?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Java 17, from the Blitz readme on github "Use JDK 17 or higher. For building Markup Blitz" |
||
| <version>1.12</version> | ||
| </dependency> | ||
|
|
||
| <!-- test dependencies --> | ||
| <dependency> | ||
| <groupId>xyz.elemental.fork.org.exist-db</groupId> | ||
|
|
@@ -793,6 +799,8 @@ | |
| <include>src/test/java/org/exist/xquery/functions/xquery3/SerializeTest.java</include> | ||
| <include>src/main/java/org/exist/xquery/value/ArrayWrapper.java</include> | ||
| <include>src/test/java/org/exist/xquery/value/DateTimeTypesTest.java</include> | ||
| <include>src/main/java/org/exist/xquery/functions/fn/FnInvisibleXml.java</include> | ||
| <include>src/test/java/org/exist/xquery/functions/fn/FnInvisibleXmlTest.java</include> | ||
| </includes> | ||
| </licenseSet> | ||
|
|
||
|
|
@@ -2339,6 +2347,8 @@ | |
| <exclude>src/main/java/org/exist/xquery/functions/fn/FnFormatDates.java</exclude> | ||
| <exclude>src/main/java/org/exist/xquery/functions/fn/FnHasChildren.java</exclude> | ||
| <exclude>src/main/java/org/exist/xquery/functions/fn/FnInnerMost.java</exclude> | ||
| <exclude>src/main/java/org/exist/xquery/functions/fn/FnInvisibleXml.java</exclude> | ||
| <exclude>src/test/java/org/exist/xquery/functions/fn/FnInvisibleXmlTest.java</exclude> | ||
| <exclude>src/main/java/org/exist/xquery/functions/fn/FnModule.java</exclude> | ||
| <exclude>src/main/java/org/exist/xquery/functions/fn/FnOuterMost.java</exclude> | ||
| <exclude>src/main/java/org/exist/xquery/functions/fn/FunAbs.java</exclude> | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,210 @@ | ||
| /* | ||
| * Elemental | ||
| * Copyright (C) 2024, Evolved Binary Ltd | ||
| * | ||
| * admin@evolvedbinary.com | ||
| * https://www.evolvedbinary.com | https://www.elemental.xyz | ||
| * | ||
| * 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; version 2.1. | ||
| * | ||
| * 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.xquery.functions.fn; | ||
|
|
||
| import static com.evolvedbinary.j8fu.Either.Left; | ||
| import static com.evolvedbinary.j8fu.Either.Right; | ||
| import static org.exist.xquery.FunctionDSL.optParam; | ||
| import static org.exist.xquery.functions.fn.FnModule.functionSignature; | ||
| import static org.exist.xquery.FunctionDSL.param; | ||
| import static org.exist.xquery.FunctionDSL.returns; | ||
|
|
||
| import com.evolvedbinary.j8fu.Either; | ||
| import org.exist.Namespaces; | ||
| import org.apache.commons.io.output.StringBuilderWriter; | ||
| import org.exist.dom.memtree.SAXAdapter; | ||
| import org.exist.util.XMLReaderPool; | ||
| import org.exist.util.serializer.XQuerySerializer; | ||
| import org.exist.xquery.*; | ||
| import org.exist.xquery.functions.map.MapType; | ||
| import org.exist.xquery.value.*; | ||
| import org.w3c.dom.Element; | ||
| import org.xml.sax.*; | ||
|
|
||
| import de.bottlecaps.markup.Blitz; | ||
| import de.bottlecaps.markup.BlitzException; | ||
| import de.bottlecaps.markup.blitz.Parser; | ||
|
|
||
| import javax.annotation.Nullable; | ||
| import java.io.IOException; | ||
| import java.io.StringReader; | ||
| import java.util.Properties; | ||
|
|
||
| public class FnInvisibleXml extends BasicFunction { | ||
|
|
||
| private static final String FS_INVISIBLE_XML_NAME = "invisible-xml"; | ||
|
|
||
| final static FunctionSignature FS_INVISIBLE_XML = functionSignature( | ||
| FS_INVISIBLE_XML_NAME, | ||
| "Evaluates invisible XML.", | ||
| returns(Type.FUNCTION, "The iXML parsing function"), | ||
| optParam("grammar", Type.ITEM, "The iXML grammar"), | ||
| optParam("options", Type.MAP_ITEM, "Options for the iXML parser")); | ||
|
|
||
| public FnInvisibleXml(final XQueryContext context, final FunctionSignature signature) { | ||
| super(context, signature); | ||
| } | ||
|
|
||
| @Override | ||
| public Sequence eval(final Sequence[] args, final Sequence contextSequence) throws XPathException { | ||
| final Sequence optionsArg = args[1]; | ||
| final MapType options = optionsArg.isEmpty() | ||
| ? new MapType(context) | ||
| : (MapType) optionsArg.itemAt(0); | ||
|
|
||
| final IxmlParserFunction fn; | ||
| final Sequence grammarArg = args[0]; | ||
| if (grammarArg.isEmpty()) { | ||
| // no grammar provided | ||
| fn = new IxmlParserFunction(context, options); | ||
|
|
||
| } else if (grammarArg.getItemType() == Type.STRING) { | ||
| // grammar is a string | ||
| final StringValue grammarString = grammarArg.itemAt(0).toJavaObject(StringValue.class); | ||
| fn = new IxmlParserFunction(context, grammarString, options); | ||
|
|
||
| } else { | ||
| // grammar is an element | ||
| final Element grammarItem = grammarArg.itemAt(0).toJavaObject(Element.class); | ||
| fn = new IxmlParserFunction(context, grammarItem, options); | ||
| } | ||
|
|
||
| final FunctionCall invisibleXmlFunctionCall = new FunctionCall(context, fn); | ||
| return new FunctionReference(invisibleXmlFunctionCall); | ||
| } | ||
|
|
||
| private static class IxmlParserFunction extends UserDefinedFunction { | ||
|
|
||
| private static final String FS_PARSE_INVISIBLE_XML_NAME = "parse-invisible-xml"; | ||
| private static final FunctionSignature FS_PARSE_INVISIBLE_XML = functionSignature( | ||
| FS_PARSE_INVISIBLE_XML_NAME, | ||
| "Parses the input using the given iXML grammar.", | ||
| returns(Type.DOCUMENT, "The parsed document"), | ||
| param("input", Type.STRING, "The input to parse")); | ||
|
|
||
| private static final StringValue FAIL_ON_ERROR_KEY = new StringValue("fail-on-error"); | ||
|
|
||
| @Nullable | ||
| final Either<StringValue, Element> grammar; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. At the moment, What we really have in the code at the moment is: I think it might be better to change the code so that
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed in b6dbf1c |
||
| final MapType options; | ||
|
|
||
| IxmlParserFunction(final XQueryContext context, final MapType options) throws XPathException { | ||
| super(context, FS_PARSE_INVISIBLE_XML); | ||
| addVariable("input"); | ||
| this.grammar = null; | ||
| this.options = options; | ||
| } | ||
|
|
||
| IxmlParserFunction(final XQueryContext context, final StringValue grammar, | ||
| final MapType options) throws XPathException { | ||
| super(context, FS_PARSE_INVISIBLE_XML); | ||
| addVariable("input"); | ||
| this.grammar = Left(grammar); | ||
| this.options = options; | ||
| } | ||
|
|
||
| IxmlParserFunction(final XQueryContext context, final Element grammar, | ||
| final MapType options) throws XPathException { | ||
| super(context, FS_PARSE_INVISIBLE_XML); | ||
| addVariable("input"); | ||
| this.grammar = Right(grammar); | ||
| this.options = options; | ||
| } | ||
|
|
||
| @Override | ||
| public Sequence eval(final Sequence contextSequence, final Item contextItem) throws XPathException { | ||
|
|
||
| // get the input | ||
| final Sequence inputArg = getCurrentArguments()[0]; | ||
| final String input = inputArg.getStringValue(); | ||
|
|
||
| final String ixmlGrammar; | ||
| if (grammar == null) { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am not sure that
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed in b6dbf1c |
||
| // no grammar provided: use the default Invisible XML specification grammar | ||
| try { | ||
| ixmlGrammar = Blitz.ixmlGrammar(); | ||
| } catch (final BlitzException e) { | ||
| throw new XPathException(this, ErrorCodes.W3CErrorCode.FOIX0003.getErrorCode(), e.getMessage(), e); | ||
| } | ||
| } else if (grammar.isLeft()) { | ||
| ixmlGrammar = grammar.left().get().getStringValue(); | ||
| } else { | ||
| // grammar is an element: serialize it to a String | ||
| try (final StringBuilderWriter writer = new StringBuilderWriter()) { | ||
| final XQuerySerializer xqSerializer = new XQuerySerializer( | ||
| context.getBroker(), new Properties(), writer); | ||
| xqSerializer.serialize((Sequence) grammar.right().get()); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would serialization be avoidable if we used CoffeePot instead of Blitz?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No it would still happen. |
||
| ixmlGrammar = writer.toString(); | ||
| } catch (final SAXException e) { | ||
| throw new XPathException(this, ErrorCodes.W3CErrorCode.FOIX0001.getErrorCode(), e.getMessage(), e); | ||
| } | ||
| } | ||
|
|
||
| final boolean failOnError = options.contains(FAIL_ON_ERROR_KEY) | ||
| && options.get(FAIL_ON_ERROR_KEY).effectiveBooleanValue(); | ||
|
|
||
| final Parser parser; | ||
| try { | ||
| parser = Blitz.generate(ixmlGrammar); | ||
| } catch (final BlitzException e) { | ||
| throw new XPathException(this, ErrorCodes.W3CErrorCode.FOIX0001.getErrorCode(), e.getMessage(), e); | ||
| } | ||
|
|
||
| // parse the input using the ixml grammar | ||
| final String generatedXML; | ||
| try { | ||
| generatedXML = failOnError | ||
| ? parser.parse(input, Blitz.Option.FAIL_ON_ERROR) | ||
| : parser.parse(input); | ||
| } catch (final BlitzException e) { | ||
| throw new XPathException(this, ErrorCodes.W3CErrorCode.FOIX0002.getErrorCode(), e.getMessage(), e); | ||
| } | ||
|
|
||
| return parse(generatedXML); | ||
| } | ||
|
|
||
| @Override | ||
| public void accept(final ExpressionVisitor visitor) { | ||
| if (visited) { | ||
| return; | ||
| } | ||
| visited = true; | ||
| } | ||
|
|
||
| private Sequence parse(final String xmlContent) throws XPathException { | ||
| final XMLReaderPool pool = context.getBroker().getBrokerPool().getParserPool(); | ||
| final SAXAdapter adapter = new SAXAdapter(context); | ||
| final XMLReader reader = pool.borrowXMLReader(); | ||
| try (final StringReader stringReader = new StringReader(xmlContent)) { | ||
| reader.setContentHandler(adapter); | ||
| reader.setProperty(Namespaces.SAX_LEXICAL_HANDLER, adapter); | ||
| reader.parse(new InputSource(stringReader)); | ||
| return adapter.getDocument(); | ||
| } catch (final SAXException | IOException e) { | ||
| throw new XPathException(this, e.getMessage(), e); | ||
| } finally { | ||
| pool.returnXMLReader(reader); | ||
| } | ||
|
|
||
| } | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You need to update the license plugin file details in
exist-core/pom.xmlpleaseThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
updated in bed1ba4