net.sf.saxon.expr.CompareToStringConstant Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of Saxon-HE Show documentation
Show all versions of Saxon-HE Show documentation
The XSLT and XQuery Processor
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2018-2023 Saxonica Limited
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
package net.sf.saxon.expr;
import net.sf.saxon.expr.elab.BooleanEvaluator;
import net.sf.saxon.expr.elab.UnicodeStringEvaluator;
import net.sf.saxon.expr.elab.BooleanElaborator;
import net.sf.saxon.expr.elab.Elaborator;
import net.sf.saxon.expr.parser.ExpressionTool;
import net.sf.saxon.expr.parser.RebindingMap;
import net.sf.saxon.expr.parser.Token;
import net.sf.saxon.expr.sort.AtomicComparer;
import net.sf.saxon.expr.sort.CodepointCollatingComparer;
import net.sf.saxon.expr.sort.CodepointCollator;
import net.sf.saxon.lib.StringCollator;
import net.sf.saxon.str.UnicodeString;
import net.sf.saxon.trace.ExpressionPresenter;
import net.sf.saxon.trans.XPathException;
import net.sf.saxon.value.Cardinality;
/**
* This class implements a comparison of a computed value to a string constant using one of the operators
* eq, ne, lt, gt, le, ge. The semantics are identical to ValueComparison, with code-point collation,
* but this is a fast path for an important common case.
*/
public class CompareToStringConstant extends CompareToConstant {
private final UnicodeString comparand;
/**
* Create the expression
*
* @param operand the operand to be compared with an integer constant. This must
* have a static type of NUMERIC, and a cardinality of EXACTLY ONE
* @param operator the comparison operator,
* one of {@link Token#FEQ}, {@link Token#FNE}, {@link Token#FGE},
* {@link Token#FGT}, {@link Token#FLE}, {@link Token#FLT}
* @param comparand the integer constant
*/
public CompareToStringConstant(Expression operand, int operator, UnicodeString comparand) {
super(operand);
this.operator = operator;
this.comparand = comparand;
}
/**
* Get the string value on the rhs of the expression
*
* @return the integer constant
*/
public UnicodeString getComparand() {
return comparand;
}
/**
* Get the effective right-hand-side expression (so that general logic for comparison expressions
* can be used)
* @return a Literal representing the RHS expression
*/
@Override
public Expression getRhsExpression() {
return new StringLiteral(comparand);
}
/**
* Copy an expression. This makes a deep copy.
*
* @return the copy of the original expression
* @param rebindings variables that need to be re-bound
*/
/*@NotNull*/
@Override
public Expression copy(RebindingMap rebindings) {
CompareToStringConstant c2 = new CompareToStringConstant(getLhsExpression().copy(rebindings), operator, comparand);
ExpressionTool.copyLocationInfo(this, c2);
return c2;
}
/**
* Is this expression the same as another expression?
*
* @param other the expression to be compared with this one
* @return true if the two expressions are statically equivalent
*/
@Override
public boolean equals(Object other) {
return other instanceof CompareToStringConstant && ((CompareToStringConstant)other).getLhsExpression().isEqual(getLhsExpression())
&& ((CompareToStringConstant)other).comparand.equals(comparand)
&& ((CompareToStringConstant)other).operator == operator;
}
/**
* Hashcode supporting equals()
*/
@Override
protected int computeHashCode() {
int h = 0x484b12a0;
return h + getLhsExpression().hashCode() ^ comparand.hashCode();
}
/**
* Get the effective boolean value of the expression. This returns false if the value
* is the empty sequence, a zero-length string, a number equal to zero, or the boolean
* false. Otherwise it returns true.
*
* @param context The context in which the expression is to be evaluated
* @return the effective boolean value
* @throws XPathException
* if any dynamic error occurs evaluating the
* expression
*/
@Override
public boolean effectiveBooleanValue(XPathContext context) throws XPathException {
UnicodeString s = getLhsExpression().evaluateItem(context).getUnicodeStringValue();
int c = CodepointCollator.getInstance().compareStrings(s, comparand);
return interpretComparisonResult(operator, c);
}
@Override
protected int computeCardinality() {
return StaticProperty.EXACTLY_ONE;
}
/**
* Get a name identifying the kind of expression, in terms meaningful to a user.
*
* @return a name identifying the kind of expression, in terms meaningful to a user.
* The name will always be in the form of a lexical XML QName, and should match the name used
* in export() output displaying the expression.
*/
@Override
public String getExpressionName() {
return "compareToString";
}
/**
* Diagnostic print of expression structure. The abstract expression tree
* is written to the supplied output destination.
*/
@Override
public void export(ExpressionPresenter destination) throws XPathException {
destination.startElement("compareToString", this);
destination.emitAttribute("op", Token.tokens[operator]);
destination.emitAttribute("val", getComparand().toString());
getLhsExpression().export(destination);
destination.endElement();
}
/**
* The toString() method for an expression attempts to give a representation of the expression
* in an XPath-like form.
* For subclasses of Expression that represent XPath expressions, the result should always be a string that
* parses as an XPath 3.0 expression
*
* @return a representation of the expression as a string
*/
@Override
public String toString() {
return ExpressionTool.parenthesize(getLhsExpression()) + " " +
Token.tokens[operator] + " " + comparand.toString();
}
/**
* Produce a short string identifying the expression for use in error messages
*
* @return a short string, sufficient to identify the expression
*/
@Override
public String toShortString() {
return getLhsExpression().toShortString() + " " + Token.tokens[operator] + " \"" + comparand + "\"";
}
/**
* Get the AtomicComparer used to compare atomic values. This encapsulates any collation that is used
*/
@Override
public AtomicComparer getAtomicComparer() {
return CodepointCollatingComparer.getInstance();
}
/**
* Get the StringCollator used to compare string values.
* @return the collator.
*/
@Override
public StringCollator getStringCollator() {
return CodepointCollator.getInstance();
}
/**
* Make an elaborator for this expression
*
* @return a suitable elaborator
*/
@Override
public Elaborator getElaborator() {
return new CompareToStringConstantElaborator();
}
/**
* Elaborator for a "compare to string constant" expression
*/
public static class CompareToStringConstantElaborator extends BooleanElaborator {
public BooleanEvaluator elaborateForBoolean() {
final CompareToStringConstant expression = (CompareToStringConstant) getExpression();
final Expression arg = expression.getBaseExpression();
final UnicodeStringEvaluator argEval = arg.makeElaborator().elaborateForUnicodeString(false);
final boolean nullable = Cardinality.allowsZero(expression.getCardinality());
final int operator = expression.getComparisonOperator();
final UnicodeString comparand = expression.getComparand();
return context -> {
UnicodeString value = argEval.eval(context);
if (nullable && value == null) {
return false;
}
int c = value.compareTo(comparand);
return interpretComparisonResult(operator, c);
};
}
}
}