blob: 0d8e6c88e19ef6b807d3d6703848ec3c059065aa [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "FunctionSubstring.hpp"
#include <xalanc/PlatformSupport/DoubleSupport.hpp>
#include <xalanc/PlatformSupport/XalanMessageLoader.hpp>
#include "XObjectFactory.hpp"
namespace XALAN_CPP_NAMESPACE {
const XObjectPtr FunctionSubstring::s_nullXObjectPtr;
FunctionSubstring::FunctionSubstring()
{
}
FunctionSubstring::~FunctionSubstring()
{
}
/*
* Get the value for the start index (C-style, not XPath). This
* function expects theSecondArgValue to be already rounded.
*/
inline XalanDOMString::size_type
getStartIndex(
double theSecondArgValue,
XalanDOMString::size_type theStringLength)
{
// We always subtract 1 for C-style index, since
// XPath indexes from 1.
// If we end up with NaN, INF, or -INF, then no possible index
// can be greater than or equal to that, so just return
// the start index as the length of the string. That
// will result in an empty string, which is what we want.
if (DoubleSupport::isNaN(theSecondArgValue) == true ||
DoubleSupport::isPositiveInfinity(theSecondArgValue) == true ||
DoubleSupport::isNegativeInfinity(theSecondArgValue) == true)
{
return theStringLength;
}
// Anything less than, or equal to 1 is 0.
else if (DoubleSupport::lessThanOrEqual(theSecondArgValue, 1) == true)
{
assert(DoubleSupport::round(theSecondArgValue) == theSecondArgValue);
return 0;
}
else
{
assert(DoubleSupport::round(theSecondArgValue) == theSecondArgValue);
// Since we have filtered out anything less than
// 1, and any special values, we can do this without
// calling DoubleSupport::subtract().
const double theResult = theSecondArgValue - 1;
assert(
DoubleSupport::equal(
DoubleSupport::subtract(
theSecondArgValue,
1),
theResult));
return XalanDOMString::size_type(theResult);
}
}
/*
* Get the length of the substring.
*/
inline XalanDOMString::size_type
getSubstringLength(
XPathExecutionContext& executionContext,
XalanDOMString::size_type theSourceStringLength,
XalanDOMString::size_type theStartIndex,
double theSecondArgValue,
const XObjectPtr& arg3)
{
assert(theStartIndex < theSourceStringLength);
assert(DoubleSupport::isNaN(theSecondArgValue) == false);
assert(DoubleSupport::isNegativeInfinity(theSecondArgValue) == false);
assert(DoubleSupport::isPositiveInfinity(theSecondArgValue) == false);
typedef XalanDOMString::size_type size_type;
const size_type theMaxLength =
theSourceStringLength - theStartIndex;
// Total the second and third arguments. If the third argument is
// missing, make it the length of the string + 1 (for XPath
// indexing style).
if (arg3.null() == true)
{
return theMaxLength;
}
else
{
const double theThirdArgValue = arg3->num(executionContext);
if (DoubleSupport::isNaN(theThirdArgValue) == true ||
DoubleSupport::isNegativeInfinity(theThirdArgValue) == true)
{
return 0;
}
else if (DoubleSupport::isPositiveInfinity(theThirdArgValue) == true)
{
return theMaxLength;
}
else
{
const double theRoundedValue =
DoubleSupport::round(theThirdArgValue);
assert(DoubleSupport::isNaN(theRoundedValue) == false);
assert(DoubleSupport::isNegativeInfinity(theRoundedValue) == false);
assert(DoubleSupport::isPositiveInfinity(theRoundedValue) == false);
// The XPath recommendation states that following:
//
// http://www.w3.org/TR/xpath#function-substring
//
// "The returned substring contains those characters for which the
// position of the character is greater than or equal to the rounded
// value of the second argument and, if the third argument is specified,
// less than the sum of the rounded value of the second argument and the
// rounded value of the third argument; the comparisons and addition used
// for the above follow the standard IEEE 754 rules; rounding is done as
// if by a call to the round function."
//
// Note that this is indexing from 1.
const double theTotal =
theRoundedValue + theSecondArgValue;
const size_type theXPathStartIndex =
theStartIndex + 1;
// If the total is less than or equal to
// the starting index, or greater
// than or equal to the starting index, the
// substring is empty.
if (theTotal <= theXPathStartIndex)
{
return 0;
}
else
{
const size_type theSubstringLength =
size_type(theTotal) - theXPathStartIndex;
return theSubstringLength > theMaxLength ? theMaxLength : theSubstringLength;
}
}
}
}
static const XalanDOMString theEmptyString(XalanMemMgrs::getDummyMemMgr());
inline XObjectPtr
createEmptyString(XPathExecutionContext& executionContext)
{
return executionContext.getXObjectFactory().createStringReference(theEmptyString);
}
XObjectPtr
FunctionSubstring::execute(
XPathExecutionContext& executionContext,
XalanNode* context,
const XObjectPtr arg1,
const XObjectPtr arg2,
const Locator* locator) const
{
assert(arg1.null() == false && arg2.null() == false);
return execute(executionContext, context, arg1, arg2, s_nullXObjectPtr, locator);
}
XObjectPtr
FunctionSubstring::execute(
XPathExecutionContext& executionContext,
XalanNode* /* context */,
const XObjectPtr arg1,
const XObjectPtr arg2,
const XObjectPtr arg3,
const Locator* const /* locator */) const
{
assert(arg1.null() == false && arg2.null() == false);
const XalanDOMString& theSourceString = arg1->str(executionContext);
const XalanDOMString::size_type theSourceStringLength = theSourceString.length();
if (theSourceStringLength == 0)
{
return createEmptyString(executionContext);
}
else
{
// Get the value of the second argument...
const double theSecondArgValue =
DoubleSupport::round(arg2->num(executionContext));
// XPath indexes from 1, so this is the first XPath index....
const XalanDOMString::size_type theStartIndex =
getStartIndex(theSecondArgValue, theSourceStringLength);
if (theStartIndex >= theSourceStringLength)
{
return createEmptyString(executionContext);
}
else
{
assert(DoubleSupport::isNaN(theSecondArgValue) == false);
assert(DoubleSupport::isPositiveInfinity(theSecondArgValue) == false);
const XalanDOMString::size_type theSubstringLength =
getSubstringLength(
executionContext,
theSourceStringLength,
theStartIndex,
theSecondArgValue,
arg3);
if (theSubstringLength == 0)
{
return createEmptyString(executionContext);
}
else
{
GetCachedString theResult(executionContext);
XalanDOMString& theString = theResult.get();
theString.assign(
theSourceString.c_str() + theStartIndex,
theSubstringLength);
return executionContext.getXObjectFactory().createString(theResult);
}
}
}
}
FunctionSubstring*
FunctionSubstring::clone(MemoryManager& theManager) const
{
return XalanCopyConstruct(theManager, *this);
}
const XalanDOMString&
FunctionSubstring::getError(XalanDOMString& theResult) const
{
return XalanMessageLoader::getMessage(
theResult,
XalanMessages::FunctionTakesTwoOrThreeArguments_1Param,
"substring()");
}
}