michael@0: /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "txExpr.h" michael@0: #include "txNodeSet.h" michael@0: #include "txIXPathContext.h" michael@0: #include "txXPathTreeWalker.h" michael@0: michael@0: /** michael@0: * Compares the two ExprResults based on XPath 1.0 Recommendation (section 3.4) michael@0: */ michael@0: bool michael@0: RelationalExpr::compareResults(txIEvalContext* aContext, txAExprResult* aLeft, michael@0: txAExprResult* aRight) michael@0: { michael@0: short ltype = aLeft->getResultType(); michael@0: short rtype = aRight->getResultType(); michael@0: nsresult rv = NS_OK; michael@0: michael@0: // Handle case for just Left NodeSet or Both NodeSets michael@0: if (ltype == txAExprResult::NODESET) { michael@0: if (rtype == txAExprResult::BOOLEAN) { michael@0: BooleanResult leftBool(aLeft->booleanValue()); michael@0: return compareResults(aContext, &leftBool, aRight); michael@0: } michael@0: michael@0: txNodeSet* nodeSet = static_cast(aLeft); michael@0: nsRefPtr strResult; michael@0: rv = aContext->recycler()->getStringResult(getter_AddRefs(strResult)); michael@0: NS_ENSURE_SUCCESS(rv, false); michael@0: michael@0: int32_t i; michael@0: for (i = 0; i < nodeSet->size(); ++i) { michael@0: strResult->mValue.Truncate(); michael@0: txXPathNodeUtils::appendNodeValue(nodeSet->get(i), michael@0: strResult->mValue); michael@0: if (compareResults(aContext, strResult, aRight)) { michael@0: return true; michael@0: } michael@0: } michael@0: michael@0: return false; michael@0: } michael@0: michael@0: // Handle case for Just Right NodeSet michael@0: if (rtype == txAExprResult::NODESET) { michael@0: if (ltype == txAExprResult::BOOLEAN) { michael@0: BooleanResult rightBool(aRight->booleanValue()); michael@0: return compareResults(aContext, aLeft, &rightBool); michael@0: } michael@0: michael@0: txNodeSet* nodeSet = static_cast(aRight); michael@0: nsRefPtr strResult; michael@0: rv = aContext->recycler()->getStringResult(getter_AddRefs(strResult)); michael@0: NS_ENSURE_SUCCESS(rv, false); michael@0: michael@0: int32_t i; michael@0: for (i = 0; i < nodeSet->size(); ++i) { michael@0: strResult->mValue.Truncate(); michael@0: txXPathNodeUtils::appendNodeValue(nodeSet->get(i), michael@0: strResult->mValue); michael@0: if (compareResults(aContext, aLeft, strResult)) { michael@0: return true; michael@0: } michael@0: } michael@0: michael@0: return false; michael@0: } michael@0: michael@0: // Neither is a NodeSet michael@0: if (mOp == EQUAL || mOp == NOT_EQUAL) { michael@0: bool result; michael@0: const nsString *lString, *rString; michael@0: michael@0: // If either is a bool, compare as bools. michael@0: if (ltype == txAExprResult::BOOLEAN || michael@0: rtype == txAExprResult::BOOLEAN) { michael@0: result = aLeft->booleanValue() == aRight->booleanValue(); michael@0: } michael@0: michael@0: // If either is a number, compare as numbers. michael@0: else if (ltype == txAExprResult::NUMBER || michael@0: rtype == txAExprResult::NUMBER) { michael@0: double lval = aLeft->numberValue(); michael@0: double rval = aRight->numberValue(); michael@0: result = (lval == rval); michael@0: } michael@0: michael@0: // Otherwise compare as strings. Try to use the stringobject in michael@0: // StringResult if possible since that is a common case. michael@0: else if ((lString = aLeft->stringValuePointer())) { michael@0: if ((rString = aRight->stringValuePointer())) { michael@0: result = lString->Equals(*rString); michael@0: } michael@0: else { michael@0: nsAutoString rStr; michael@0: aRight->stringValue(rStr); michael@0: result = lString->Equals(rStr); michael@0: } michael@0: } michael@0: else if ((rString = aRight->stringValuePointer())) { michael@0: nsAutoString lStr; michael@0: aLeft->stringValue(lStr); michael@0: result = rString->Equals(lStr); michael@0: } michael@0: else { michael@0: nsAutoString lStr, rStr; michael@0: aLeft->stringValue(lStr); michael@0: aRight->stringValue(rStr); michael@0: result = lStr.Equals(rStr); michael@0: } michael@0: michael@0: return mOp == EQUAL ? result : !result; michael@0: } michael@0: michael@0: double leftDbl = aLeft->numberValue(); michael@0: double rightDbl = aRight->numberValue(); michael@0: switch (mOp) { michael@0: case LESS_THAN: michael@0: { michael@0: return (leftDbl < rightDbl); michael@0: } michael@0: case LESS_OR_EQUAL: michael@0: { michael@0: return (leftDbl <= rightDbl); michael@0: } michael@0: case GREATER_THAN: michael@0: { michael@0: return (leftDbl > rightDbl); michael@0: } michael@0: case GREATER_OR_EQUAL: michael@0: { michael@0: return (leftDbl >= rightDbl); michael@0: } michael@0: default: michael@0: { michael@0: NS_NOTREACHED("We should have caught all cases"); michael@0: } michael@0: } michael@0: michael@0: return false; michael@0: } michael@0: michael@0: nsresult michael@0: RelationalExpr::evaluate(txIEvalContext* aContext, txAExprResult** aResult) michael@0: { michael@0: *aResult = nullptr; michael@0: nsRefPtr lResult; michael@0: nsresult rv = mLeftExpr->evaluate(aContext, getter_AddRefs(lResult)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: nsRefPtr rResult; michael@0: rv = mRightExpr->evaluate(aContext, getter_AddRefs(rResult)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: aContext->recycler()-> michael@0: getBoolResult(compareResults(aContext, lResult, rResult), aResult); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: TX_IMPL_EXPR_STUBS_2(RelationalExpr, BOOLEAN_RESULT, mLeftExpr, mRightExpr) michael@0: michael@0: bool michael@0: RelationalExpr::isSensitiveTo(ContextSensitivity aContext) michael@0: { michael@0: return mLeftExpr->isSensitiveTo(aContext) || michael@0: mRightExpr->isSensitiveTo(aContext); michael@0: } michael@0: michael@0: #ifdef TX_TO_STRING michael@0: void michael@0: RelationalExpr::toString(nsAString& str) michael@0: { michael@0: mLeftExpr->toString(str); michael@0: michael@0: switch (mOp) { michael@0: case NOT_EQUAL: michael@0: str.AppendLiteral("!="); michael@0: break; michael@0: case LESS_THAN: michael@0: str.Append(char16_t('<')); michael@0: break; michael@0: case LESS_OR_EQUAL: michael@0: str.AppendLiteral("<="); michael@0: break; michael@0: case GREATER_THAN : michael@0: str.Append(char16_t('>')); michael@0: break; michael@0: case GREATER_OR_EQUAL: michael@0: str.AppendLiteral(">="); michael@0: break; michael@0: default: michael@0: str.Append(char16_t('=')); michael@0: break; michael@0: } michael@0: michael@0: mRightExpr->toString(str); michael@0: } michael@0: #endif