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 "mozilla/ArrayUtils.h" michael@0: #include "mozilla/FloatingPoint.h" michael@0: michael@0: #include "txExpr.h" michael@0: #include "nsAutoPtr.h" michael@0: #include "txNodeSet.h" michael@0: #include "nsGkAtoms.h" michael@0: #include "txIXPathContext.h" michael@0: #include "nsWhitespaceTokenizer.h" michael@0: #include "txXPathTreeWalker.h" michael@0: #include michael@0: #include "txStringUtils.h" michael@0: #include "txXMLUtils.h" michael@0: michael@0: using namespace mozilla; michael@0: michael@0: struct txCoreFunctionDescriptor michael@0: { michael@0: int8_t mMinParams; michael@0: int8_t mMaxParams; michael@0: Expr::ResultType mReturnType; michael@0: nsIAtom** mName; michael@0: }; michael@0: michael@0: // This must be ordered in the same order as txCoreFunctionCall::eType. michael@0: // If you change one, change the other. michael@0: static const txCoreFunctionDescriptor descriptTable[] = michael@0: { michael@0: { 1, 1, Expr::NUMBER_RESULT, &nsGkAtoms::count }, // COUNT michael@0: { 1, 1, Expr::NODESET_RESULT, &nsGkAtoms::id }, // ID michael@0: { 0, 0, Expr::NUMBER_RESULT, &nsGkAtoms::last }, // LAST michael@0: { 0, 1, Expr::STRING_RESULT, &nsGkAtoms::localName }, // LOCAL_NAME michael@0: { 0, 1, Expr::STRING_RESULT, &nsGkAtoms::namespaceUri }, // NAMESPACE_URI michael@0: { 0, 1, Expr::STRING_RESULT, &nsGkAtoms::name }, // NAME michael@0: { 0, 0, Expr::NUMBER_RESULT, &nsGkAtoms::position }, // POSITION michael@0: michael@0: { 2, -1, Expr::STRING_RESULT, &nsGkAtoms::concat }, // CONCAT michael@0: { 2, 2, Expr::BOOLEAN_RESULT, &nsGkAtoms::contains }, // CONTAINS michael@0: { 0, 1, Expr::STRING_RESULT, &nsGkAtoms::normalizeSpace }, // NORMALIZE_SPACE michael@0: { 2, 2, Expr::BOOLEAN_RESULT, &nsGkAtoms::startsWith }, // STARTS_WITH michael@0: { 0, 1, Expr::STRING_RESULT, &nsGkAtoms::string }, // STRING michael@0: { 0, 1, Expr::NUMBER_RESULT, &nsGkAtoms::stringLength }, // STRING_LENGTH michael@0: { 2, 3, Expr::STRING_RESULT, &nsGkAtoms::substring }, // SUBSTRING michael@0: { 2, 2, Expr::STRING_RESULT, &nsGkAtoms::substringAfter }, // SUBSTRING_AFTER michael@0: { 2, 2, Expr::STRING_RESULT, &nsGkAtoms::substringBefore }, // SUBSTRING_BEFORE michael@0: { 3, 3, Expr::STRING_RESULT, &nsGkAtoms::translate }, // TRANSLATE michael@0: michael@0: { 0, 1, Expr::NUMBER_RESULT, &nsGkAtoms::number }, // NUMBER michael@0: { 1, 1, Expr::NUMBER_RESULT, &nsGkAtoms::round }, // ROUND michael@0: { 1, 1, Expr::NUMBER_RESULT, &nsGkAtoms::floor }, // FLOOR michael@0: { 1, 1, Expr::NUMBER_RESULT, &nsGkAtoms::ceiling }, // CEILING michael@0: { 1, 1, Expr::NUMBER_RESULT, &nsGkAtoms::sum }, // SUM michael@0: michael@0: { 1, 1, Expr::BOOLEAN_RESULT, &nsGkAtoms::boolean }, // BOOLEAN michael@0: { 0, 0, Expr::BOOLEAN_RESULT, &nsGkAtoms::_false }, // _FALSE michael@0: { 1, 1, Expr::BOOLEAN_RESULT, &nsGkAtoms::lang }, // LANG michael@0: { 1, 1, Expr::BOOLEAN_RESULT, &nsGkAtoms::_not }, // _NOT michael@0: { 0, 0, Expr::BOOLEAN_RESULT, &nsGkAtoms::_true } // _TRUE michael@0: }; michael@0: michael@0: michael@0: /* michael@0: * Evaluates this Expr based on the given context node and processor state michael@0: * @param context the context node for evaluation of this Expr michael@0: * @param ps the ContextState containing the stack information needed michael@0: * for evaluation michael@0: * @return the result of the evaluation michael@0: */ michael@0: nsresult michael@0: txCoreFunctionCall::evaluate(txIEvalContext* aContext, txAExprResult** aResult) michael@0: { michael@0: *aResult = nullptr; michael@0: michael@0: if (!requireParams(descriptTable[mType].mMinParams, michael@0: descriptTable[mType].mMaxParams, michael@0: aContext)) { michael@0: return NS_ERROR_XPATH_BAD_ARGUMENT_COUNT; michael@0: } michael@0: michael@0: nsresult rv = NS_OK; michael@0: switch (mType) { michael@0: case COUNT: michael@0: { michael@0: nsRefPtr nodes; michael@0: rv = evaluateToNodeSet(mParams[0], aContext, michael@0: getter_AddRefs(nodes)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: return aContext->recycler()->getNumberResult(nodes->size(), michael@0: aResult); michael@0: } michael@0: case ID: michael@0: { michael@0: nsRefPtr exprResult; michael@0: rv = mParams[0]->evaluate(aContext, getter_AddRefs(exprResult)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: nsRefPtr resultSet; michael@0: rv = aContext->recycler()->getNodeSet(getter_AddRefs(resultSet)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: txXPathTreeWalker walker(aContext->getContextNode()); michael@0: michael@0: if (exprResult->getResultType() == txAExprResult::NODESET) { michael@0: txNodeSet* nodes = static_cast michael@0: (static_cast michael@0: (exprResult)); michael@0: int32_t i; michael@0: for (i = 0; i < nodes->size(); ++i) { michael@0: nsAutoString idList; michael@0: txXPathNodeUtils::appendNodeValue(nodes->get(i), idList); michael@0: nsWhitespaceTokenizer tokenizer(idList); michael@0: while (tokenizer.hasMoreTokens()) { michael@0: if (walker.moveToElementById(tokenizer.nextToken())) { michael@0: resultSet->add(walker.getCurrentPosition()); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: else { michael@0: nsAutoString idList; michael@0: exprResult->stringValue(idList); michael@0: nsWhitespaceTokenizer tokenizer(idList); michael@0: while (tokenizer.hasMoreTokens()) { michael@0: if (walker.moveToElementById(tokenizer.nextToken())) { michael@0: resultSet->add(walker.getCurrentPosition()); michael@0: } michael@0: } michael@0: } michael@0: michael@0: *aResult = resultSet; michael@0: NS_ADDREF(*aResult); michael@0: michael@0: return NS_OK; michael@0: } michael@0: case LAST: michael@0: { michael@0: return aContext->recycler()->getNumberResult(aContext->size(), michael@0: aResult); michael@0: } michael@0: case LOCAL_NAME: michael@0: case NAME: michael@0: case NAMESPACE_URI: michael@0: { michael@0: // Check for optional arg michael@0: nsRefPtr nodes; michael@0: if (!mParams.IsEmpty()) { michael@0: rv = evaluateToNodeSet(mParams[0], aContext, michael@0: getter_AddRefs(nodes)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: if (nodes->isEmpty()) { michael@0: aContext->recycler()->getEmptyStringResult(aResult); michael@0: michael@0: return NS_OK; michael@0: } michael@0: } michael@0: michael@0: const txXPathNode& node = nodes ? nodes->get(0) : michael@0: aContext->getContextNode(); michael@0: switch (mType) { michael@0: case LOCAL_NAME: michael@0: { michael@0: StringResult* strRes = nullptr; michael@0: rv = aContext->recycler()->getStringResult(&strRes); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: *aResult = strRes; michael@0: txXPathNodeUtils::getLocalName(node, strRes->mValue); michael@0: michael@0: return NS_OK; michael@0: } michael@0: case NAMESPACE_URI: michael@0: { michael@0: StringResult* strRes = nullptr; michael@0: rv = aContext->recycler()->getStringResult(&strRes); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: *aResult = strRes; michael@0: txXPathNodeUtils::getNamespaceURI(node, strRes->mValue); michael@0: michael@0: return NS_OK; michael@0: } michael@0: case NAME: michael@0: { michael@0: // XXX Namespace: namespaces have a name michael@0: if (txXPathNodeUtils::isAttribute(node) || michael@0: txXPathNodeUtils::isElement(node) || michael@0: txXPathNodeUtils::isProcessingInstruction(node)) { michael@0: StringResult* strRes = nullptr; michael@0: rv = aContext->recycler()->getStringResult(&strRes); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: *aResult = strRes; michael@0: txXPathNodeUtils::getNodeName(node, strRes->mValue); michael@0: } michael@0: else { michael@0: aContext->recycler()->getEmptyStringResult(aResult); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: default: michael@0: { michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: case POSITION: michael@0: { michael@0: return aContext->recycler()->getNumberResult(aContext->position(), michael@0: aResult); michael@0: } michael@0: michael@0: // String functions michael@0: michael@0: case CONCAT: michael@0: { michael@0: nsRefPtr strRes; michael@0: rv = aContext->recycler()->getStringResult(getter_AddRefs(strRes)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: uint32_t i, len = mParams.Length(); michael@0: for (i = 0; i < len; ++i) { michael@0: rv = mParams[i]->evaluateToString(aContext, strRes->mValue); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: michael@0: NS_ADDREF(*aResult = strRes); michael@0: michael@0: return NS_OK; michael@0: } michael@0: case CONTAINS: michael@0: { michael@0: nsAutoString arg2; michael@0: rv = mParams[1]->evaluateToString(aContext, arg2); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: if (arg2.IsEmpty()) { michael@0: aContext->recycler()->getBoolResult(true, aResult); michael@0: } michael@0: else { michael@0: nsAutoString arg1; michael@0: rv = mParams[0]->evaluateToString(aContext, arg1); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: aContext->recycler()->getBoolResult(FindInReadable(arg2, arg1), michael@0: aResult); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: case NORMALIZE_SPACE: michael@0: { michael@0: nsAutoString resultStr; michael@0: if (!mParams.IsEmpty()) { michael@0: rv = mParams[0]->evaluateToString(aContext, resultStr); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: else { michael@0: txXPathNodeUtils::appendNodeValue(aContext->getContextNode(), michael@0: resultStr); michael@0: } michael@0: michael@0: nsRefPtr strRes; michael@0: rv = aContext->recycler()->getStringResult(getter_AddRefs(strRes)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: bool addSpace = false; michael@0: bool first = true; michael@0: strRes->mValue.SetCapacity(resultStr.Length()); michael@0: char16_t c; michael@0: uint32_t src; michael@0: for (src = 0; src < resultStr.Length(); src++) { michael@0: c = resultStr.CharAt(src); michael@0: if (XMLUtils::isWhitespace(c)) { michael@0: addSpace = true; michael@0: } michael@0: else { michael@0: if (addSpace && !first) michael@0: strRes->mValue.Append(char16_t(' ')); michael@0: michael@0: strRes->mValue.Append(c); michael@0: addSpace = false; michael@0: first = false; michael@0: } michael@0: } michael@0: *aResult = strRes; michael@0: NS_ADDREF(*aResult); michael@0: michael@0: return NS_OK; michael@0: } michael@0: case STARTS_WITH: michael@0: { michael@0: nsAutoString arg2; michael@0: rv = mParams[1]->evaluateToString(aContext, arg2); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: bool result = false; michael@0: if (arg2.IsEmpty()) { michael@0: result = true; michael@0: } michael@0: else { michael@0: nsAutoString arg1; michael@0: rv = mParams[0]->evaluateToString(aContext, arg1); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: result = StringBeginsWith(arg1, arg2); michael@0: } michael@0: michael@0: aContext->recycler()->getBoolResult(result, aResult); michael@0: michael@0: return NS_OK; michael@0: } michael@0: case STRING: michael@0: { michael@0: nsRefPtr strRes; michael@0: rv = aContext->recycler()->getStringResult(getter_AddRefs(strRes)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: if (!mParams.IsEmpty()) { michael@0: rv = mParams[0]->evaluateToString(aContext, strRes->mValue); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: else { michael@0: txXPathNodeUtils::appendNodeValue(aContext->getContextNode(), michael@0: strRes->mValue); michael@0: } michael@0: michael@0: NS_ADDREF(*aResult = strRes); michael@0: michael@0: return NS_OK; michael@0: } michael@0: case STRING_LENGTH: michael@0: { michael@0: nsAutoString resultStr; michael@0: if (!mParams.IsEmpty()) { michael@0: rv = mParams[0]->evaluateToString(aContext, resultStr); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: else { michael@0: txXPathNodeUtils::appendNodeValue(aContext->getContextNode(), michael@0: resultStr); michael@0: } michael@0: rv = aContext->recycler()->getNumberResult(resultStr.Length(), michael@0: aResult); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: return NS_OK; michael@0: } michael@0: case SUBSTRING: michael@0: { michael@0: nsAutoString src; michael@0: rv = mParams[0]->evaluateToString(aContext, src); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: double start; michael@0: rv = evaluateToNumber(mParams[1], aContext, &start); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: // check for NaN or +/-Inf michael@0: if (mozilla::IsNaN(start) || michael@0: mozilla::IsInfinite(start) || michael@0: start >= src.Length() + 0.5) { michael@0: aContext->recycler()->getEmptyStringResult(aResult); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: start = floor(start + 0.5) - 1; michael@0: michael@0: double end; michael@0: if (mParams.Length() == 3) { michael@0: rv = evaluateToNumber(mParams[2], aContext, &end); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: end += start; michael@0: if (mozilla::IsNaN(end) || end < 0) { michael@0: aContext->recycler()->getEmptyStringResult(aResult); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: if (end > src.Length()) michael@0: end = src.Length(); michael@0: else michael@0: end = floor(end + 0.5); michael@0: } michael@0: else { michael@0: end = src.Length(); michael@0: } michael@0: michael@0: if (start < 0) michael@0: start = 0; michael@0: michael@0: if (start > end) { michael@0: aContext->recycler()->getEmptyStringResult(aResult); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: return aContext->recycler()->getStringResult( michael@0: Substring(src, (uint32_t)start, (uint32_t)(end - start)), michael@0: aResult); michael@0: } michael@0: case SUBSTRING_AFTER: michael@0: { michael@0: nsAutoString arg1; michael@0: rv = mParams[0]->evaluateToString(aContext, arg1); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: nsAutoString arg2; michael@0: rv = mParams[1]->evaluateToString(aContext, arg2); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: if (arg2.IsEmpty()) { michael@0: return aContext->recycler()->getStringResult(arg1, aResult); michael@0: } michael@0: michael@0: int32_t idx = arg1.Find(arg2); michael@0: if (idx == kNotFound) { michael@0: aContext->recycler()->getEmptyStringResult(aResult); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: const nsSubstring& result = Substring(arg1, idx + arg2.Length()); michael@0: return aContext->recycler()->getStringResult(result, aResult); michael@0: } michael@0: case SUBSTRING_BEFORE: michael@0: { michael@0: nsAutoString arg2; michael@0: rv = mParams[1]->evaluateToString(aContext, arg2); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: if (arg2.IsEmpty()) { michael@0: aContext->recycler()->getEmptyStringResult(aResult); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsAutoString arg1; michael@0: rv = mParams[0]->evaluateToString(aContext, arg1); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: int32_t idx = arg1.Find(arg2); michael@0: if (idx == kNotFound) { michael@0: aContext->recycler()->getEmptyStringResult(aResult); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: return aContext->recycler()->getStringResult(StringHead(arg1, idx), michael@0: aResult); michael@0: } michael@0: case TRANSLATE: michael@0: { michael@0: nsAutoString src; michael@0: rv = mParams[0]->evaluateToString(aContext, src); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: if (src.IsEmpty()) { michael@0: aContext->recycler()->getEmptyStringResult(aResult); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsRefPtr strRes; michael@0: rv = aContext->recycler()->getStringResult(getter_AddRefs(strRes)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: strRes->mValue.SetCapacity(src.Length()); michael@0: michael@0: nsAutoString oldChars, newChars; michael@0: rv = mParams[1]->evaluateToString(aContext, oldChars); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = mParams[2]->evaluateToString(aContext, newChars); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: uint32_t i; michael@0: int32_t newCharsLength = (int32_t)newChars.Length(); michael@0: for (i = 0; i < src.Length(); i++) { michael@0: int32_t idx = oldChars.FindChar(src.CharAt(i)); michael@0: if (idx != kNotFound) { michael@0: if (idx < newCharsLength) michael@0: strRes->mValue.Append(newChars.CharAt((uint32_t)idx)); michael@0: } michael@0: else { michael@0: strRes->mValue.Append(src.CharAt(i)); michael@0: } michael@0: } michael@0: michael@0: NS_ADDREF(*aResult = strRes); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Number functions michael@0: michael@0: case NUMBER: michael@0: { michael@0: double res; michael@0: if (!mParams.IsEmpty()) { michael@0: rv = evaluateToNumber(mParams[0], aContext, &res); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: else { michael@0: nsAutoString resultStr; michael@0: txXPathNodeUtils::appendNodeValue(aContext->getContextNode(), michael@0: resultStr); michael@0: res = txDouble::toDouble(resultStr); michael@0: } michael@0: return aContext->recycler()->getNumberResult(res, aResult); michael@0: } michael@0: case ROUND: michael@0: { michael@0: double dbl; michael@0: rv = evaluateToNumber(mParams[0], aContext, &dbl); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: if (mozilla::IsFinite(dbl)) { michael@0: if (mozilla::IsNegative(dbl) && dbl >= -0.5) { michael@0: dbl *= 0; michael@0: } michael@0: else { michael@0: dbl = floor(dbl + 0.5); michael@0: } michael@0: } michael@0: michael@0: return aContext->recycler()->getNumberResult(dbl, aResult); michael@0: } michael@0: case FLOOR: michael@0: { michael@0: double dbl; michael@0: rv = evaluateToNumber(mParams[0], aContext, &dbl); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: if (mozilla::IsFinite(dbl) && !mozilla::IsNegativeZero(dbl)) michael@0: dbl = floor(dbl); michael@0: michael@0: return aContext->recycler()->getNumberResult(dbl, aResult); michael@0: } michael@0: case CEILING: michael@0: { michael@0: double dbl; michael@0: rv = evaluateToNumber(mParams[0], aContext, &dbl); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: if (mozilla::IsFinite(dbl)) { michael@0: if (mozilla::IsNegative(dbl) && dbl > -1) michael@0: dbl *= 0; michael@0: else michael@0: dbl = ceil(dbl); michael@0: } michael@0: michael@0: return aContext->recycler()->getNumberResult(dbl, aResult); michael@0: } michael@0: case SUM: michael@0: { michael@0: nsRefPtr nodes; michael@0: nsresult rv = evaluateToNodeSet(mParams[0], aContext, michael@0: getter_AddRefs(nodes)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: double res = 0; michael@0: int32_t i; michael@0: for (i = 0; i < nodes->size(); ++i) { michael@0: nsAutoString resultStr; michael@0: txXPathNodeUtils::appendNodeValue(nodes->get(i), resultStr); michael@0: res += txDouble::toDouble(resultStr); michael@0: } michael@0: return aContext->recycler()->getNumberResult(res, aResult); michael@0: } michael@0: michael@0: // Boolean functions michael@0: michael@0: case BOOLEAN: michael@0: { michael@0: bool result; michael@0: nsresult rv = mParams[0]->evaluateToBool(aContext, result); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: aContext->recycler()->getBoolResult(result, aResult); michael@0: michael@0: return NS_OK; michael@0: } michael@0: case _FALSE: michael@0: { michael@0: aContext->recycler()->getBoolResult(false, aResult); michael@0: michael@0: return NS_OK; michael@0: } michael@0: case LANG: michael@0: { michael@0: txXPathTreeWalker walker(aContext->getContextNode()); michael@0: michael@0: nsAutoString lang; michael@0: bool found; michael@0: do { michael@0: found = walker.getAttr(nsGkAtoms::lang, kNameSpaceID_XML, michael@0: lang); michael@0: } while (!found && walker.moveToParent()); michael@0: michael@0: if (!found) { michael@0: aContext->recycler()->getBoolResult(false, aResult); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsAutoString arg; michael@0: rv = mParams[0]->evaluateToString(aContext, arg); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: bool result = michael@0: StringBeginsWith(lang, arg, michael@0: txCaseInsensitiveStringComparator()) && michael@0: (lang.Length() == arg.Length() || michael@0: lang.CharAt(arg.Length()) == '-'); michael@0: michael@0: aContext->recycler()->getBoolResult(result, aResult); michael@0: michael@0: return NS_OK; michael@0: } michael@0: case _NOT: michael@0: { michael@0: bool result; michael@0: rv = mParams[0]->evaluateToBool(aContext, result); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: aContext->recycler()->getBoolResult(!result, aResult); michael@0: michael@0: return NS_OK; michael@0: } michael@0: case _TRUE: michael@0: { michael@0: aContext->recycler()->getBoolResult(true, aResult); michael@0: michael@0: return NS_OK; michael@0: } michael@0: } michael@0: michael@0: aContext->receiveError(NS_LITERAL_STRING("Internal error"), michael@0: NS_ERROR_UNEXPECTED); michael@0: return NS_ERROR_UNEXPECTED; michael@0: } michael@0: michael@0: Expr::ResultType michael@0: txCoreFunctionCall::getReturnType() michael@0: { michael@0: return descriptTable[mType].mReturnType; michael@0: } michael@0: michael@0: bool michael@0: txCoreFunctionCall::isSensitiveTo(ContextSensitivity aContext) michael@0: { michael@0: switch (mType) { michael@0: case COUNT: michael@0: case CONCAT: michael@0: case CONTAINS: michael@0: case STARTS_WITH: michael@0: case SUBSTRING: michael@0: case SUBSTRING_AFTER: michael@0: case SUBSTRING_BEFORE: michael@0: case TRANSLATE: michael@0: case ROUND: michael@0: case FLOOR: michael@0: case CEILING: michael@0: case SUM: michael@0: case BOOLEAN: michael@0: case _NOT: michael@0: case _FALSE: michael@0: case _TRUE: michael@0: { michael@0: return argsSensitiveTo(aContext); michael@0: } michael@0: case ID: michael@0: { michael@0: return (aContext & NODE_CONTEXT) || michael@0: argsSensitiveTo(aContext); michael@0: } michael@0: case LAST: michael@0: { michael@0: return !!(aContext & SIZE_CONTEXT); michael@0: } michael@0: case LOCAL_NAME: michael@0: case NAME: michael@0: case NAMESPACE_URI: michael@0: case NORMALIZE_SPACE: michael@0: case STRING: michael@0: case STRING_LENGTH: michael@0: case NUMBER: michael@0: { michael@0: if (mParams.IsEmpty()) { michael@0: return !!(aContext & NODE_CONTEXT); michael@0: } michael@0: return argsSensitiveTo(aContext); michael@0: } michael@0: case POSITION: michael@0: { michael@0: return !!(aContext & POSITION_CONTEXT); michael@0: } michael@0: case LANG: michael@0: { michael@0: return (aContext & NODE_CONTEXT) || michael@0: argsSensitiveTo(aContext); michael@0: } michael@0: } michael@0: michael@0: NS_NOTREACHED("how'd we get here?"); michael@0: return true; michael@0: } michael@0: michael@0: // static michael@0: bool michael@0: txCoreFunctionCall::getTypeFromAtom(nsIAtom* aName, eType& aType) michael@0: { michael@0: uint32_t i; michael@0: for (i = 0; i < ArrayLength(descriptTable); ++i) { michael@0: if (aName == *descriptTable[i].mName) { michael@0: aType = static_cast(i); michael@0: michael@0: return true; michael@0: } michael@0: } michael@0: michael@0: return false; michael@0: } michael@0: michael@0: #ifdef TX_TO_STRING michael@0: nsresult michael@0: txCoreFunctionCall::getNameAtom(nsIAtom** aAtom) michael@0: { michael@0: NS_ADDREF(*aAtom = *descriptTable[mType].mName); michael@0: return NS_OK; michael@0: } michael@0: #endif