Skip to content
Draft
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
6 changes: 6 additions & 0 deletions exist-core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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>
<version>1.12</version>
</dependency>

<!-- test dependencies -->
<dependency>
<groupId>xyz.elemental.fork.org.exist-db</groupId>
Expand Down
21 changes: 21 additions & 0 deletions exist-core/src/main/java/org/exist/xquery/ErrorCodes.java
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,9 @@ public enum W3CErrorCode implements IErrorCode {
FOXT0003 ("XSLT transformation failed"),
FOXT0004 ("XSLT transformation has been disabled"),
FOXT0006 ("XSLT output contains non-accepted characters"),
FOIX0001 ("Invalid Invisible XML grammar."),
FOIX0002 ("Input provided could not be parsed successfully."),
FOIX0003 ("No Invisible XML processor is available."),
XTSE0165 ("It is a static error if the processor is not able to retrieve the resource identified by the URI reference [ in the href attribute of xsl:include or xsl:import] , or if the resource that is retrieved does not contain a stylesheet module conforming to this specification.");

private final ErrorCode errorCode;
Expand Down Expand Up @@ -1537,6 +1540,24 @@ public DynamicErrorCode(final QName qname, @Nullable final String description) {
@Deprecated
public static final ErrorCode FOXT0006 = W3CErrorCode.FOXT0006.errorCode;

/**
* @deprecated Use {@link W3CErrorCode#FOIX0001}.
*/
@Deprecated
public static final ErrorCode FOIX0001 = W3CErrorCode.FOIX0001.errorCode;

/**
* @deprecated Use {@link W3CErrorCode#FOIX0002}.
*/
@Deprecated
public static final ErrorCode FOIX0002 = W3CErrorCode.FOIX0002.errorCode;

/**
* @deprecated Use {@link W3CErrorCode#FOIX0003}.
*/
@Deprecated
public static final ErrorCode FOIX0003 = W3CErrorCode.FOIX0003.errorCode;

/**
* @deprecated Use {@link W3CErrorCode#XTSE0165}.
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
/*
* 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.exist.dom.memtree.SAXAdapter;
import org.exist.util.XMLReaderPool;
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 javax.annotation.Nullable;
import java.io.IOException;
import java.io.StringReader;

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];
@Nullable final MapType options;
if (optionsArg.isEmpty()) {
options = null;
} else {
options = optionsArg.itemAt(0).toJavaObject(MapType.class);
}

final IxmlParserFunction fn;
final Sequence grammarArg = args[0];
if (grammarArg.isEmpty()) {
// no grammar provided
fn = new IxmlParserFunction(context, (StringValue) null, 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 grammarElement = grammarArg.itemAt(0).toJavaObject(Element.class);
fn = new IxmlParserFunction(context, grammarElement, options);
}

final InlineFunction functionResult = new InlineFunction(context, fn);
return functionResult.eval(contextSequence, null);
}

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,
"Gets the next random number generator.",
returns(Type.DOCUMENT, "just a random string for now"),
param("Parser inpuit", Type.STRING, "param description")
);

@Nullable final Either<StringValue, Element> grammar;
@Nullable final MapType options;

IxmlParserFunction(final XQueryContext context, @Nullable final StringValue grammar, @Nullable final MapType options) {
super(context, FS_PARSE_INVISIBLE_XML);
this.grammar = Left(grammar);
this.options = options;
}

IxmlParserFunction(final XQueryContext context, @Nullable final Element grammar, @Nullable final MapType options) {
super(context, FS_PARSE_INVISIBLE_XML);
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 = getArguments(contextSequence, contextItem)[0];
final String input = inputArg.itemAt(0).getStringValue();

// generate the default ixml grammar
final String ixmlGrammar;
if (grammar == null) {
ixmlGrammar = Blitz.ixmlGrammar();
} else if (grammar.isLeft()) {
ixmlGrammar = grammar.left().get().getStringValue();
} else {
// TODO(YB) use XQuerySerializer class to serialize Element to String
ixmlGrammar = ...
}

// TODO(YB) set any options

// parse the input using the ixml grammar
final String generatedXML = Blitz.generate(ixmlGrammar).parse(input);

return parse(generatedXML);
}

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) {
// TODO(YB) add error code
throw new XPathException(this, e.getMessage(), e);
} finally {
pool.returnXMLReader(reader);
}

}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,8 @@ public class FnModule extends AbstractInternalModule {
new FunctionDef(FnRandomNumberGenerator.FS_RANDOM_NUMBER_GENERATOR[0], FnRandomNumberGenerator.class),
new FunctionDef(FnRandomNumberGenerator.FS_RANDOM_NUMBER_GENERATOR[1], FnRandomNumberGenerator.class),
new FunctionDef(FunContainsToken.FS_CONTAINS_TOKEN[0], FunContainsToken.class),
new FunctionDef(FunContainsToken.FS_CONTAINS_TOKEN[1], FunContainsToken.class)
new FunctionDef(FunContainsToken.FS_CONTAINS_TOKEN[1], FunContainsToken.class),
new FunctionDef(FnInvisibleXml.FS_INVISIBLE_XML, FnInvisibleXml.class)
};

static {
Expand Down
Loading