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
577 changes: 568 additions & 9 deletions exist-core/src/main/antlr/org/exist/xquery/parser/XQuery.g

Large diffs are not rendered by default.

622 changes: 622 additions & 0 deletions exist-core/src/main/antlr/org/exist/xquery/parser/XQueryTree.g

Large diffs are not rendered by default.

13 changes: 13 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 @@ -243,6 +243,19 @@ public class ErrorCodes {

public static final ErrorCode XTSE0165 = new W3CErrorCode("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.");

// W3C XQuery and XPath Full Text 3.0 error codes
public static final ErrorCode FTST0001 = new W3CErrorCode("FTST0001", "It is a static error if an operand of mild not (not in) contains ftnot or occurs.");
public static final ErrorCode FTST0003 = new W3CErrorCode("FTST0003", "It is a static error if a tokenizer for the language specified by the language option is not available.");
public static final ErrorCode FTST0004 = new W3CErrorCode("FTST0004", "It is a static error if sentence/paragraph boundaries are required but not supported by the tokenizer.");
public static final ErrorCode FTST0006 = new W3CErrorCode("FTST0006", "It is a static error if a stop word list cannot be found.");
public static final ErrorCode FTST0008 = new W3CErrorCode("FTST0008", "It is a static error if a stop word list is not in the correct format.");
public static final ErrorCode FTST0009 = new W3CErrorCode("FTST0009", "It is a static error if the specified language is not supported.");
public static final ErrorCode FTDY0016 = new W3CErrorCode("FTDY0016", "It is a dynamic error if a weight value is not within the required range.");
public static final ErrorCode FTDY0017 = new W3CErrorCode("FTDY0017", "It is a dynamic error if the right-hand match of mild not has any include-matches matching tokens not matched by include-matches of the left-hand match.");
public static final ErrorCode FTST0013 = new W3CErrorCode("FTST0013", "It is a static error if, in an implementation which does not support the Stop Word Languages feature, a stop word option includes a language specification.");
public static final ErrorCode FTST0018 = new W3CErrorCode("FTST0018", "It is a static error if a thesaurus is not available.");
public static final ErrorCode FTST0019 = new W3CErrorCode("FTST0019", "It is a static error if match options in a single contains text expression conflict with each other.");

/* eXist specific XQuery and XPath errors
*
* Codes have the format [EX][XQ|XP][DY|SE|ST][nnnn]
Expand Down
38 changes: 38 additions & 0 deletions exist-core/src/main/java/org/exist/xquery/ForExpr.java
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
public class ForExpr extends BindingExpression {

private QName positionalVariable = null;
private QName scoreVariable = null;
private boolean allowEmpty = false;
private boolean isOuterFor = true;

Expand All @@ -60,6 +61,17 @@ public void setPositionalVariable(final QName variable) {
positionalVariable = variable;
}

/**
* XQFT 3.0 §2.3: A "for" expression may have an optional score variable
* whose QName can be set via this method. The score variable is bound to
* an xs:double value representing the relevance score for each item.
*
* @param variable the name of the score variable
*/
public void setScoreVariable(final QName variable) {
scoreVariable = variable;
}

/* (non-Javadoc)
* @see org.exist.xquery.Expression#analyze(org.exist.xquery.Expression)
*/
Expand All @@ -83,6 +95,13 @@ public void analyze(AnalyzeContextInfo contextInfo) throws XPathException {
posVar.setStaticType(Type.INTEGER);
context.declareVariableBinding(posVar);
}
// Declare score variable (XQFT 3.0 §2.3)
if (scoreVariable != null) {
final LocalVariable scoreVar = new LocalVariable(scoreVariable);
scoreVar.setSequenceType(new SequenceType(Type.DOUBLE, Cardinality.EXACTLY_ONE));
scoreVar.setStaticType(Type.DOUBLE);
context.declareVariableBinding(scoreVar);
}

final AnalyzeContextInfo newContextInfo = new AnalyzeContextInfo(contextInfo);
newContextInfo.addFlag(SINGLE_STEP_EXECUTION);
Expand Down Expand Up @@ -135,6 +154,15 @@ public Sequence eval(Sequence contextSequence, Item contextItem)
at.setSequenceType(POSITIONAL_VAR_TYPE);
context.declareVariableBinding(at);
}
// Declare score variable (XQFT 3.0 §2.3)
LocalVariable score = null;
if (scoreVariable != null) {
score = new LocalVariable(scoreVariable);
score.setSequenceType(new SequenceType(Type.DOUBLE, Cardinality.EXACTLY_ONE));
context.declareVariableBinding(score);
// Naive implementation: always bind score to 1.0
score.setValue(new DoubleValue(this, 1.0));
}
// Assign the whole input sequence to the bound variable.
// This is required if we process the "where" or "order by" clause
// in one step.
Expand Down Expand Up @@ -238,6 +266,8 @@ private boolean callPostEval() {
case ORDERBY:
case GROUPBY:
return true;
default:
break;
}
prev = prev.getPreviousClause();
}
Expand All @@ -264,6 +294,8 @@ public void dump(ExpressionDumper dumper) {
}
if (positionalVariable != null)
{dumper.display(" at ").display(positionalVariable);}
if (scoreVariable != null)
{dumper.display(" score ").display(scoreVariable);}
dumper.display(" in ");
inputSequence.dump(dumper);
dumper.endIndent().nl();
Expand All @@ -290,6 +322,9 @@ public String toString() {
if (positionalVariable != null) {
result.append(" at ").append(positionalVariable);
}
if (scoreVariable != null) {
result.append(" score ").append(scoreVariable);
}
result.append(" in ");
result.append(inputSequence.toString());
result.append(" ");
Expand All @@ -313,6 +348,9 @@ public Set<QName> getTupleStreamVariables() {
if (positionalVariable != null) {
variables.add(positionalVariable);
}
if (scoreVariable != null) {
variables.add(scoreVariable);
}

final QName variable = getVariable();
if (variable != null) {
Expand Down
21 changes: 19 additions & 2 deletions exist-core/src/main/java/org/exist/xquery/LetExpr.java
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,21 @@
*/
public class LetExpr extends BindingExpression {

private boolean scoreBinding = false;

public LetExpr(XQueryContext context) {
super(context);
}

/**
* XQFT 3.0 §2.3: Mark this let binding as a score variable binding.
* When true, the variable is bound to the score (xs:double in [0,1])
* of the input expression rather than the expression's value.
*/
public void setScoreBinding(final boolean scoreBinding) {
this.scoreBinding = scoreBinding;
}

@Override
public ClauseType getType() {
return ClauseType.LET;
Expand Down Expand Up @@ -102,9 +113,15 @@ public Sequence eval(Sequence contextSequence, Item contextItem)
var = createVariable(varName);
var.setSequenceType(sequenceType);
context.declareVariableBinding(var);
var.setValue(in);
if (scoreBinding) {
// XQFT 3.0 §2.3: score binding — bind variable to the score
// of the expression. Naive implementation: 1.0 if non-empty, 0.0 if empty.
var.setValue(new DoubleValue(this, in.isEmpty() ? 0.0 : 1.0));
} else {
var.setValue(in);
}
if (sequenceType == null)
{var.checkType();} //Just because it makes conversions !
{var.checkType();} //Just because it makes conversions !
var.setContextDocs(inputSequence.getContextDocSet());
registerUpdateListener(in);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
*/
package org.exist.xquery;

import org.exist.xquery.ErrorCodes.ErrorCode;

public class StaticXQueryException extends XPathException
{
private static final long serialVersionUID = -8229758099980343418L;
Expand Down Expand Up @@ -53,7 +55,15 @@ public StaticXQueryException(final Expression expression, String message, Throwa
super(expression, message, cause);
}

//TODO add in ErrorCode and ErrorVal
public StaticXQueryException(int line, int column, ErrorCode errorCode, String message) {
super(line, column, errorCode, message);
}

public StaticXQueryException(int line, int column, ErrorCode errorCode, String message, Throwable cause) {
super(line, column, errorCode, message);
initCause(cause);
}

public StaticXQueryException(int line, int column, String message, Throwable cause) {
super(line, column, message, cause);
}
Expand Down
4 changes: 2 additions & 2 deletions exist-core/src/main/java/org/exist/xquery/XQuery.java
Original file line number Diff line number Diff line change
Expand Up @@ -288,7 +288,7 @@ private CompiledXQuery compile(final XQueryContext context, final Reader reader,
if (msg.endsWith(", found 'null'")) {
msg = msg.substring(0, msg.length() - ", found 'null'".length());
}
throw new StaticXQueryException(e.getLine(), e.getColumn(), msg);
throw new StaticXQueryException(e.getLine(), e.getColumn(), ErrorCodes.XPST0003, msg);
} catch(final TokenStreamException e) {
final String es = e.toString();
if(es.matches("^line \\d+:\\d+:.+")) {
Expand All @@ -298,7 +298,7 @@ private CompiledXQuery compile(final XQueryContext context, final Reader reader,
final int line = Integer.parseInt(es.substring(5, es.indexOf(':')));
final String tmpColumn = es.substring(es.indexOf(':') + 1);
final int column = Integer.parseInt(tmpColumn.substring(0, tmpColumn.indexOf(':')));
throw new StaticXQueryException(line, column, e.getMessage(), e);
throw new StaticXQueryException(line, column, ErrorCodes.XPST0003, e.getMessage(), e);
} else {
if (LOG.isDebugEnabled()) {
LOG.debug("Error compiling query: {}", e.getMessage(), e);
Expand Down
30 changes: 30 additions & 0 deletions exist-core/src/main/java/org/exist/xquery/XQueryContext.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@
import java.net.URISyntaxException;
import java.nio.charset.Charset;
import java.nio.file.Path;

import org.exist.xquery.ft.FTMatchOptions;
import java.nio.file.Paths;
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
Expand Down Expand Up @@ -307,6 +309,18 @@ public class XQueryContext implements BinaryValueManager, Context {
*/
private String defaultCollation = Collations.UNICODE_CODEPOINT_COLLATION_URI;

/**
* XQFT 3.0: default full-text match options declared via "declare ft-option".
*/
private FTMatchOptions defaultFTMatchOptions;

/**
* XQFT 3.0: thesaurus URI-to-file mapping.
* Maps thesaurus URIs (e.g., "http://bstore1.example.com/UsabilityThesaurus.xml")
* to local file paths.
*/
private final Map<String, Path> thesaurusRegistry = new HashMap<>();

/**
* The default language
*/
Expand Down Expand Up @@ -1090,6 +1104,22 @@ public String getDefaultCollation() {
return defaultCollation;
}

public void setDefaultFTMatchOptions(final FTMatchOptions opts) {
this.defaultFTMatchOptions = opts;
}

public FTMatchOptions getDefaultFTMatchOptions() {
return defaultFTMatchOptions;
}

public void registerThesaurus(final String uri, final Path file) {
thesaurusRegistry.put(uri, file);
}

public Path resolveThesaurusURI(final String uri) {
return thesaurusRegistry.get(uri);
}

@Override
public Collator getCollator(String uri) throws XPathException {
return getCollator(uri, ErrorCodes.XQST0076);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
* 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.xquery.ft;

import org.exist.xquery.AbstractExpression;
import org.exist.xquery.XPathException;
import org.exist.xquery.XQueryContext;
import org.exist.xquery.value.Item;
import org.exist.xquery.value.Sequence;
import org.exist.xquery.value.Type;

/**
* Abstract base class for Full Text expression nodes.
*
* FT expression nodes participate in the expression tree for analysis
* and serialization but are not independently evaluable — evaluation
* is driven by {@link FTContainsExpr}.
*/
public abstract class FTAbstractExpr extends AbstractExpression {

protected FTAbstractExpr(final XQueryContext context) {
super(context);
}

@Override
public Sequence eval(final Sequence contextSequence, final Item contextItem) throws XPathException {
throw new XPathException(this, getClass().getSimpleName() + " cannot be evaluated directly");
}

@Override
public int returnsType() {
return Type.ITEM;
}
}
92 changes: 92 additions & 0 deletions exist-core/src/main/java/org/exist/xquery/ft/FTAnd.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
/*
* 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.xquery.ft;

import org.exist.xquery.AnalyzeContextInfo;
import org.exist.xquery.Expression;
import org.exist.xquery.XPathException;
import org.exist.xquery.XQueryContext;
import org.exist.xquery.util.ExpressionDumper;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
* W3C XQFT 3.0 — FTAnd.
*
* <pre>FTAnd ::= FTMildNot ( "ftand" FTMildNot )*</pre>
*/
public class FTAnd extends FTAbstractExpr {

private final List<Expression> operands = new ArrayList<>();

public FTAnd(final XQueryContext context) {
super(context);
}

public void addOperand(final Expression operand) {
operands.add(operand);
}

public List<Expression> getOperands() {
return Collections.unmodifiableList(operands);
}

@Override
public void analyze(final AnalyzeContextInfo contextInfo) throws XPathException {
contextInfo.setParent(this);
for (final Expression operand : operands) {
operand.analyze(contextInfo);
}
}

@Override
public void dump(final ExpressionDumper dumper) {
for (int i = 0; i < operands.size(); i++) {
if (i > 0) {
dumper.display(" ftand ");
}
operands.get(i).dump(dumper);
}
}

@Override
public String toString() {
final StringBuilder sb = new StringBuilder();
for (int i = 0; i < operands.size(); i++) {
if (i > 0) {
sb.append(" ftand ");
}
sb.append(operands.get(i).toString());
}
return sb.toString();
}

@Override
public void resetState(final boolean postOptimization) {
super.resetState(postOptimization);
for (final Expression operand : operands) {
operand.resetState(postOptimization);
}
}
}
Loading
Loading