/*
 * 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 "ElemNumber.hpp"



#include <cstring>



#include <xercesc/sax/AttributeList.hpp>



#include <xalanc/XalanDOM/XalanDocument.hpp>



#include <xalanc/PlatformSupport/DOMStringHelper.hpp>
#include <xalanc/PlatformSupport/DoubleSupport.hpp>
#include <xalanc/PlatformSupport/XalanNumberFormat.hpp>
#include <xalanc/PlatformSupport/XalanSimplePrefixResolver.hpp>
#include <xalanc/PlatformSupport/XalanUnicode.hpp>



#include <xalanc/DOMSupport/DOMServices.hpp>

#include <xalanc/Include/XalanMemMgrAutoPtr.hpp>
#include <xalanc/Include/XalanVector.hpp>



#include <xalanc/PlatformSupport/XalanMessageLoader.hpp>



#include <xalanc/XPath/ElementPrefixResolverProxy.hpp>
#include <xalanc/XPath/XPath.hpp>
#include <xalanc/XPath/XObjectFactory.hpp>



#include "AVT.hpp"
#include "Constants.hpp"
#include "StylesheetConstructionContext.hpp"
#include "StylesheetExecutionContext.hpp"



namespace XALAN_CPP_NAMESPACE {



ElemNumber::ElemNumber(
            StylesheetConstructionContext&  constructionContext,
            Stylesheet&                     stylesheetTree,
            const AttributeListType&        atts,
            XalanFileLoc                    lineNumber, 
            XalanFileLoc                    columnNumber,
            unsigned long                   id) :
    ElemTemplateElement(constructionContext,
                        stylesheetTree,
                        lineNumber,
                        columnNumber,
                        StylesheetConstructionContext::ELEMNAME_NUMBER),    
    m_countMatchPattern(0),
    m_fromMatchPattern(0),
    m_valueExpr(0),
    m_level(eSingle),
    m_format_avt(0),
    m_lang_avt(0),
    m_lettervalue_avt(0),
    m_groupingSeparator_avt(0),
    m_groupingSize_avt(0),
    m_id(id)
    
{
    const XalanSize_t  nAttrs = atts.getLength();

    for (XalanSize_t i = 0; i < nAttrs; i++)
    {
        const XalanDOMChar* const   aname = atts.getName(i);

        if (equals(aname, s_levelString))
        {
            const XalanDOMChar* const   levelValue = atts.getValue(i);

            if (equals(s_multipleString, levelValue))
            {
                m_level = eMultiple;
            }
            else if (equals(s_anyString, levelValue))
            {
                m_level = eAny;
            }
            else if (equals(s_singleString, levelValue))
            {
                m_level = eSingle;
            }
            else
            {
                error(
                    constructionContext,
                    XalanMessages::ElementHasIllegalAttributeValue_3Param,
                    Constants::ELEMNAME_NUMBER_WITH_PREFIX_STRING.c_str(),
                    aname,
                    levelValue);
            }
        }
        else if (equals(aname, Constants::ATTRNAME_COUNT))
        {
            m_countMatchPattern = constructionContext.createMatchPattern(getLocator(), atts.getValue(i), *this);
        }
        else if (equals(aname, Constants::ATTRNAME_FROM))
        {
            m_fromMatchPattern = constructionContext.createMatchPattern(getLocator(), atts.getValue(i), *this);
        }
        else if (equals(aname, Constants::ATTRNAME_VALUE))
        {
            m_valueExpr = constructionContext.createXPath(getLocator(), atts.getValue(i), *this);
        }
        else if (equals(aname, Constants::ATTRNAME_FORMAT))
        {
            m_format_avt =
                constructionContext.createAVT(getLocator(), aname, atts.getValue(i), *this);
        }
        else if (equals(aname, Constants::ATTRNAME_LANG))
        {
            m_lang_avt =
                constructionContext.createAVT(getLocator(), aname, atts.getValue(i), *this);
        }
        else if (equals(aname, Constants::ATTRNAME_LETTERVALUE))
        {
            m_lettervalue_avt =
                constructionContext.createAVT(getLocator(), aname, atts.getValue(i), *this);
        }
        else if (equals(aname,Constants::ATTRNAME_GROUPINGSEPARATOR))
        {
            m_groupingSeparator_avt =
                constructionContext.createAVT(getLocator(), aname, atts.getValue(i), *this);
        }
        else if (equals(aname,Constants::ATTRNAME_GROUPINGSIZE))
        {
            m_groupingSize_avt =
                constructionContext.createAVT(getLocator(), aname, atts.getValue(i), *this);
        }
        else if (isAttrOK(
                    aname,
                    atts,
                    i,
                    constructionContext) == false)
        {
            error(
                constructionContext,
                XalanMessages::ElementHasIllegalAttribute_2Param,
                Constants::ELEMNAME_NUMBER_WITH_PREFIX_STRING.c_str(),
                aname);
        }
    }
}



ElemNumber*
ElemNumber::create(
            MemoryManager&                  theManager,
            StylesheetConstructionContext&  constructionContext,
            Stylesheet&                     stylesheetTree,
            const AttributeListType&        atts,
            XalanFileLoc                    lineNumber, 
            XalanFileLoc                    columnNumber,
            unsigned long                   id)
{
    typedef ElemNumber  ThisType;

    XalanAllocationGuard    theGuard(theManager, theManager.allocate(sizeof(ThisType)));

    ThisType* const     theResult =
        new (theGuard.get()) ThisType(
                                constructionContext,
                                stylesheetTree,
                                atts,
                                lineNumber,
                                columnNumber,
                                id);

    theGuard.release();

    return theResult;
}



ElemNumber::~ElemNumber()
{
}



const XalanDOMString&
ElemNumber::getElementName() const
{
    return Constants::ELEMNAME_NUMBER_WITH_PREFIX_STRING;
}



typedef XPathExecutionContext::GetCachedString  GetCachedString;

#if !defined(XALAN_RECURSIVE_STYLESHEET_EXECUTION)
const ElemTemplateElement*
ElemNumber::startElement(StylesheetExecutionContext&        executionContext) const
{
    ElemTemplateElement::startElement(executionContext);

    const GetCachedString   theGuard(executionContext);

    XalanDOMString&         countString = theGuard.get();

    getCountString(executionContext, countString);

    if (!countString.empty())
    {
        executionContext.characters(
            countString.c_str(), 0, countString.length());
    }
    
    return 0;
}
#endif



#if defined(XALAN_RECURSIVE_STYLESHEET_EXECUTION)
void
ElemNumber::execute(StylesheetExecutionContext&     executionContext) const
{
    ElemTemplateElement::execute(executionContext);

    const GetCachedString   theGuard(executionContext);

    XalanDOMString&         countString = theGuard.get();

    getCountString(executionContext, countString);

    if (!countString.empty())
    {
        executionContext.characters(countString.c_str(), 0, countString.length());
    }
}
#endif



XalanNode*
ElemNumber::findAncestor(
            StylesheetExecutionContext&     executionContext,
            const XPath*                    fromMatchPattern,
            const XPath*                    countMatchPattern,
            XalanNode*                      context) const
{
    XalanNode*  contextCopy = context;

    while (contextCopy != 0)
    {
        if (0 != fromMatchPattern)
        {
            if (fromMatchPattern->getMatchScore(
                    contextCopy,
                    *this,
                    executionContext) != XPath::eMatchScoreNone)
            {
                break;
            }
        }
        
        if (0 != countMatchPattern)
        {
            if(countMatchPattern->getMatchScore(
                    contextCopy,
                    *this,
                    executionContext) != XPath::eMatchScoreNone)
            {
                break;
            }
        }
        
        contextCopy = DOMServices::getParentOfNode(*contextCopy);
    }

    return contextCopy;
}                   



XalanNode*
ElemNumber::findPrecedingOrAncestorOrSelf(
            StylesheetExecutionContext&     executionContext,
            const XPath*                    fromMatchPattern,
            const XPath*                    countMatchPattern,
            XalanNode*                      context) const
{  
    XalanNode*  thePos = context;

    while (thePos != 0)
    {
        if (0 != fromMatchPattern)
        {
            if (fromMatchPattern->getMatchScore(
                    thePos,
                    *this,
                    executionContext) != XPath::eMatchScoreNone)
            {
                thePos = 0;
                break;
            }
        }

        if (0 != countMatchPattern)
        {
            if (countMatchPattern->getMatchScore(
                    thePos,
                    *this,
                    executionContext) != XPath::eMatchScoreNone)
            {
                break;
            }
        }

        XalanNode* const    previousSibling = thePos->getPreviousSibling();

        if (previousSibling == 0)
        {
            // Move up to the parent if thePos is the first child
            // of its parent.  Note that we don't descend into the
            // parent, since that's where we came from.
            thePos = DOMServices::getParentOfNode(*thePos);
        }
        else
        {
            thePos = previousSibling;

            // Descend into the subtree all the way to the
            // last node, which we reach by looking down
            // the last child chain.
            XalanNode* lastChild = thePos->getLastChild();

            while (lastChild != 0)
            {
                thePos = lastChild;
                lastChild = thePos->getLastChild();
            }
        }
    }

    return thePos;
}



const XPath*
ElemNumber::getCountMatchPattern(
            StylesheetExecutionContext&     executionContext,
            XalanNode*                      contextNode) const
{
    const XPath*    countMatchPattern = 0;

    switch (contextNode->getNodeType())
    {
    case XalanNode::ELEMENT_NODE:
        {
            // Check to see if we have any fiddling to do with the match pattern
            // we create...
            const XalanDOMString&   theNamespaceURI = contextNode->getNamespaceURI();
            const XalanDOMString&   theNodeName = contextNode->getNodeName();

            if (theNamespaceURI.empty() == true)
            {
                // We can pass any PrefixResolver instance, so just
                // pass ourself...
                countMatchPattern =
                        executionContext.createMatchPattern(theNodeName, *this);
            }
            else if (theNodeName.length() != contextNode->getLocalName().length())
            {
                // OK, there's a prefix, so create a prefix resolver so the
                // prefix can be properly resolved...
                const XalanElement* const   theElement =
                    static_cast<const XalanElement*>(contextNode);

                    const ElementPrefixResolverProxy    theProxy( theElement, executionContext.getMemoryManager());

                countMatchPattern =
                        executionContext.createMatchPattern(theNodeName, theProxy);
            }
            else
            {
                // OK, this is ugly.  We have to get a unique prefix and
                // construct a match pattern with that prefix...
                const GetCachedString   thePrefix(executionContext);

                executionContext.getUniqueNamespaceValue(thePrefix.get());

                const GetCachedString   theMatchPatternString(executionContext);

                theMatchPatternString.get().assign(thePrefix.get());
                theMatchPatternString.get().append(1, XalanUnicode::charColon);
                theMatchPatternString.get().append(theNodeName);

                // Use this class to resolve the synthesized prefix to the
                // appropriate namespace URI.  We could see if a prefix is
                // already in scope for the namespace URI, but it would take
                // more effort to find that out than it would to just
                // create this simple resolver.
                const XalanSimplePrefixResolver     theResolver(
                                                        thePrefix.get(),
                                                        theNamespaceURI,
                                                        getURI());

                countMatchPattern =
                        executionContext.createMatchPattern(
                            theMatchPatternString.get(),
                            theResolver);
            }
        }
        break;

    case XalanNode::ATTRIBUTE_NODE:
        {
            const XalanAttr* const  theAttribute =
                static_cast<const XalanAttr*>(contextNode);

            assert(theAttribute->getOwnerElement() != 0);

            const XalanDOMString&   theNodeName = theAttribute->getNodeName();

            const ElementPrefixResolverProxy    theProxy(theAttribute->getOwnerElement(), executionContext.getMemoryManager());

            const GetCachedString   theMatchPatternString(executionContext);

            theMatchPatternString.get().assign(s_atString);
            theMatchPatternString.get().append(theNodeName);

            countMatchPattern = executionContext.createMatchPattern(
                            theMatchPatternString.get(),
                            theProxy);
        }
        break;

    case XalanNode::CDATA_SECTION_NODE:
    case XalanNode::TEXT_NODE:
        countMatchPattern = executionContext.createMatchPattern(
                    s_textString, *this);
        break;

    case XalanNode::COMMENT_NODE:
        countMatchPattern = executionContext.createMatchPattern(
                    s_commentString, *this);
        break;

    case XalanNode::DOCUMENT_NODE:
        countMatchPattern = executionContext.createMatchPattern(
                    s_slashString, *this);
        break;

    case XalanNode::PROCESSING_INSTRUCTION_NODE:
        {
            const GetCachedString   theMatchPatternString(executionContext);

            theMatchPatternString.get() = s_piString;
            theMatchPatternString.get().append(contextNode->getNodeName());
            theMatchPatternString.get().append(1, XalanUnicode::charRightParenthesis);

            countMatchPattern = executionContext.createMatchPattern(
                    theMatchPatternString.get(),
                    *this);
        }
        break;

    default:
        break;
    }

    return countMatchPattern;
}



inline void
ElemNumber::getCountString(
            StylesheetExecutionContext&     executionContext,
            const MutableNodeRefList&       ancestors,
            CountersTable&                  ctable,
            CountType                       numberList[],
            NodeRefListBase::size_type      numberListLength,
            XalanDOMString&                 theResult) const
{
    for (NodeRefListBase::size_type i = 0; i < numberListLength; i++)
    {
        XalanNode* const target = ancestors.item(numberListLength - i - 1);

        numberList[i] = ctable.countNode(
                            executionContext,
                            *this,
                            target);
    }

    formatNumberList(
            executionContext,
            numberList,
            numberListLength,
            theResult);
}



void
ElemNumber::getCountString(
            StylesheetExecutionContext&     executionContext,
            XalanDOMString&                 theResult) const
{
    XalanNode* sourceNode = executionContext.getCurrentNode();

    assert(sourceNode != 0);

    if (0 != m_valueExpr)
    {
        double  theValue;

        m_valueExpr->execute(*this, executionContext, theValue);

        // Note these checks are in a specific order...
        if (DoubleSupport::isNaN(theValue) == true ||
            DoubleSupport::isPositiveInfinity(theValue) == true ||
            DoubleSupport::isNegativeInfinity(theValue) == true ||
            DoubleSupport::lessThan(theValue, 0.5) == true)
        {
            NumberToDOMString(theValue, theResult);
        }
        else
        {
            const CountType   theNumber =
                CountType(DoubleSupport::round(theValue));

            formatNumberList(
                    executionContext,
                    &theNumber,
                    1,
                    theResult);
        }
    }
    else
    {
        CountersTable&  ctable = executionContext.getCountersTable();

        if (eAny == m_level)
        {
            const CountType     theNumber =
                ctable.countNode(executionContext, *this, sourceNode);

            if (theNumber != 0)
            {
                formatNumberList(
                    executionContext,
                    &theNumber,
                    1,
                    theResult);
            }
        }
        else
        {
            typedef XPathExecutionContext::BorrowReturnMutableNodeRefList   BorrowReturnMutableNodeRefList;

            BorrowReturnMutableNodeRefList  ancestors(executionContext);

            getMatchingAncestors(
                executionContext,
                sourceNode,
                eSingle == m_level,
                *ancestors.get());

            const NodeRefListBase::size_type    lastIndex = ancestors->getLength();

            if (lastIndex > 0)
            {
                const NodeRefListBase::size_type    theStackArrayThreshold = 100;

                if (lastIndex < theStackArrayThreshold)
                {
                    CountType   numberList[theStackArrayThreshold];

                    getCountString(
                        executionContext,
                        *ancestors.get(),
                        ctable,
                        numberList,
                        lastIndex,
                        theResult);
                }
                else
                {
                    CountTypeArrayType  numberList(executionContext.getMemoryManager());


                    numberList.resize(lastIndex, 0);

                    getCountString(
                        executionContext,
                        *ancestors.get(),
                        ctable,
                        &*numberList.begin(),
                        lastIndex,
                        theResult);
                }
            }
        }
    }
}



XalanNode*
ElemNumber::getPreviousNode(
            StylesheetExecutionContext&     executionContext,
            XalanNode*                      pos) const
{
    // Create an XPathGuard, since we may need to
    // create a new XPath...
    StylesheetExecutionContext::XPathGuard  xpathGuard(
            executionContext);

    const XPath*    countMatchPattern = m_countMatchPattern;

    if (countMatchPattern == 0)
    {
        // Get the XPath...
        xpathGuard.reset(getCountMatchPattern(executionContext, pos));

        countMatchPattern = xpathGuard.get();
    }

    if (eAny == m_level)
    {
        const XPath* const  fromMatchPattern = m_fromMatchPattern;

        // Do a backwards document-order walk 'till a node is found that matches 
        // the 'from' pattern, or a node is found that matches the 'count' pattern, 
        // or the top of the tree is found.
        while(0 != pos)
        {
            // Get the previous sibling, if there is no previous sibling, 
            // then count the parent, but if there is a previous sibling, 
            // dive down to the lowest right-hand (last) child of that sibling.
            XalanNode* next = pos->getPreviousSibling();

            if(0 == next)
            {
                next = pos->getParentNode();

                if(0 != next &&
                   next->getNodeType() == XalanNode::DOCUMENT_NODE ||
                   (0 != fromMatchPattern &&
                        fromMatchPattern->getMatchScore(
                            next,
                            *this,
                            executionContext) != XPath::eMatchScoreNone))
                {
                    pos = 0; // return 0 from function.

                    break; // from while loop
                }
            }
            else
            {
                // dive down to the lowest right child.
                XalanNode* child = next;

                while(0 != child)
                {
                    child = next->getLastChild();

                    if(0 != child)
                        next = child;
                }
            }

            pos = next;

            if(0 != pos &&
               (0 == countMatchPattern ||
                countMatchPattern->getMatchScore(
                        pos,
                        *this,
                        executionContext) != XPath::eMatchScoreNone))
            {
                break;
            }
        }
    }
    else // NUMBERLEVEL_MULTI or NUMBERLEVEL_SINGLE
    {
        while (0 != pos)
        {            
            pos = pos->getPreviousSibling();

            if (0 != pos &&
                (0 == countMatchPattern ||
                 countMatchPattern->getMatchScore(
                        pos,
                        *this,
                        executionContext) != XPath::eMatchScoreNone))
            {
                break;
            }
        }
    }

    return pos;
}



XalanNode*
ElemNumber::getTargetNode(
        StylesheetExecutionContext&     executionContext,
        XalanNode*                      sourceNode) const
{
    XalanNode* target = 0;

    // Create an XPathGuard, since we may need to
    // create a new XPath...
    StylesheetExecutionContext::XPathGuard  xpathGuard(
            executionContext);

    const XPath*    countMatchPattern = m_countMatchPattern;

    if (countMatchPattern == 0)
    {
        // Get the XPath...
        xpathGuard.reset(getCountMatchPattern(executionContext, sourceNode));

        countMatchPattern = xpathGuard.get();
    }

    if (eAny == m_level)
    {
        target = findPrecedingOrAncestorOrSelf(
                executionContext,
                m_fromMatchPattern,
                countMatchPattern,
                sourceNode);
    }
    else
    {
        target = findAncestor(
                executionContext,
                m_fromMatchPattern,
                countMatchPattern,
                sourceNode);
    }

    return target;
}



void
ElemNumber::getMatchingAncestors(
            StylesheetExecutionContext&     executionContext,
            XalanNode*                      node, 
            bool                            stopAtFirstFound,
            MutableNodeRefList&             ancestors) const
{
    // Create an XPathGuard, since we may need to
    // create a new XPath...
    StylesheetExecutionContext::XPathGuard  xpathGuard(executionContext);

    const XPath*    countMatchPattern = m_countMatchPattern;

    if (countMatchPattern == 0)
    {
        // Get the XPath...
        xpathGuard.reset(getCountMatchPattern(executionContext, node));

        countMatchPattern = xpathGuard.get();
    }

    while (0 != node)
    {
        if (0 != m_fromMatchPattern &&
            m_fromMatchPattern->getMatchScore(
                node,
                *this,
                executionContext) != XPath::eMatchScoreNone)
        {
            // The following if statement gives level="single" different 
            // behavior from level="multiple", which seems incorrect according 
            // to the XSLT spec.  For now we are leaving this in to replicate 
            // the same behavior in XT, but, for all intents and purposes we 
            // think this is a bug, or there is something about level="single" 
            // that we still don't understand.
            if(!stopAtFirstFound)
            {
                break;
            }
        }

        assert(0 != countMatchPattern);

        if(countMatchPattern->getMatchScore(node, *this, executionContext) !=
                XPath::eMatchScoreNone)
        {
            ancestors.addNode(node);

            if (stopAtFirstFound)
            {
                break;
            }
        }

        node = DOMServices::getParentOfNode(*node);
    }
}



XalanNumberFormat*
ElemNumber::getNumberFormatter(StylesheetExecutionContext&  executionContext) const
{
    // Helper to format local specific numbers to strings.

    XalanMemMgrAutoPtr<XalanNumberFormat>   formatter(executionContext.createXalanNumberFormat());

    typedef XPathExecutionContext::GetCachedString  GetCachedString;

    const GetCachedString   theGuard1(executionContext);

    XalanDOMString&         digitGroupSepValue = theGuard1.get();

    if (0 != m_groupingSeparator_avt)
    {
        m_groupingSeparator_avt->evaluate(digitGroupSepValue, *this, executionContext);
    }
                                     
    if (digitGroupSepValue.length() > 1)
    {
        error(
            executionContext,
            XalanMessages::GroupingSeparatorValueMustBeOneCharacterLength);
    }

    GetCachedString     theGuard2(executionContext);

    XalanDOMString&     nDigitsPerGroupValue = theGuard2.get();

    if (0 != m_groupingSize_avt)
    {
        m_groupingSize_avt->evaluate(nDigitsPerGroupValue, *this, executionContext);
    }

    // 7.7.1 If one is empty, it is ignored (numb81 conf test)
    if (!digitGroupSepValue.empty() && !nDigitsPerGroupValue.empty())
    {
        formatter->setGroupingUsed(true);
        formatter->setGroupingSeparator(digitGroupSepValue);
        formatter->setGroupingSize(DOMStringToUnsignedLong(nDigitsPerGroupValue));
    }

    return formatter.releasePtr();
}



void
ElemNumber::formatNumberList(
            StylesheetExecutionContext&     executionContext,
            const CountType                 theList[],
            NodeRefListBase::size_type      theListLength,
            XalanDOMString&                 theResult) const
{
    XalanDOMChar    numberType = XalanUnicode::charDigit_1;

    XalanDOMString::size_type   numberWidth = 1;

    typedef XalanVector<XalanDOMString> StringVectorType;
    typedef StringVectorType::iterator  StringVectorTypeIterator;

    // Construct an array of tokens.  We need to be able to check if the last
    // token in non-alphabetic, in which case the penultimate non-alphabetic is
    // the repeating separator.
    //
    // We should be able to replace this with a vector of the indexes in
    // the evaluated string where the tokens start.  But for now, this will
    // have to do...
    StringVectorType    tokenVector(executionContext. getMemoryManager()) ;

    {
        const GetCachedString   theGuard1(executionContext);

        XalanDOMString&         formatValue = theGuard1.get();

        if (m_format_avt != 0)
        {
             m_format_avt->evaluate(formatValue, *this, executionContext);
        }

        if (formatValue.empty() == true)
        {
            formatValue = XalanUnicode::charDigit_1;
        }

        NumberFormatStringTokenizer                     formatTokenizer(formatValue);

        const NumberFormatStringTokenizer::size_type    theTokenCount = formatTokenizer.countTokens();

        tokenVector.resize(theTokenCount);

        // Tokenize directly into the vector...
        for (NumberFormatStringTokenizer::size_type i = 0; i < theTokenCount; ++i)
        {
            formatTokenizer.nextToken(tokenVector[i]);
        }

        assert(theTokenCount == tokenVector.size());
    }

    // These are iterators which will either point to tokenVector.end(),
    // or the appropriate string in the vector...
    StringVectorTypeIterator        leaderStrIt = tokenVector.end();
    StringVectorTypeIterator        trailerStrIt = leaderStrIt;
    StringVectorTypeIterator        sepStringIt = leaderStrIt;
    const StringVectorTypeIterator  endIt = leaderStrIt;

    StringVectorTypeIterator    it = tokenVector.begin();

    const StringVectorType::size_type   theVectorSize =
        tokenVector.size();

    if (theVectorSize > 0)
    {
        if (!isXMLLetterOrDigit((*it)[0]))
        {
            leaderStrIt = it;

            // Move the iterator up one, so it
            // points at the first numbering token...
            ++it;
        }

        if (theVectorSize > 1)
        {
            if (!isXMLLetterOrDigit(tokenVector.back()[0]))
            {
                // Move the iterator back one, so it's pointing
                // at the trailing string...
                --trailerStrIt;
            }
        }
    }

    // Now we're left with a sequence of alpha,non-alpha tokens, format them
    // with the corresponding entry in the format string, or the last one if no
    // more matching ones

    if (leaderStrIt != endIt)
    {
        theResult += *leaderStrIt;
    }

    const GetCachedString   theGuard2(executionContext);

    XalanDOMString&         theIntermediateResult = theGuard2.get();

    for (NodeRefListBase::size_type i = 0; i < theListLength; i++)
    {
        if (it != trailerStrIt)
        {
            assert(isXMLLetterOrDigit((*it)[0]));

            numberWidth = it->length();

            numberType = (*it)[numberWidth - 1];

            ++it;
        }

        if (it != trailerStrIt)
        {
            assert(!isXMLLetterOrDigit((*it)[0]));

            sepStringIt = it;

            ++it;
        }

        getFormattedNumber(
            executionContext,
            numberType,
            numberWidth,
            theList[i],
            theIntermediateResult);

        theResult += theIntermediateResult;

        // All but the last one
        if (i < theListLength - 1)
        {
            if (sepStringIt != endIt)
            {
                theResult += *sepStringIt;
            }
            else
            {
                theResult += XalanUnicode::charFullStop;
            }

            theIntermediateResult.clear();
        }
    }

    if (trailerStrIt != endIt)
    {
        theResult += *trailerStrIt;
    }
}



void
ElemNumber::evaluateLetterValueAVT(
            StylesheetExecutionContext&     executionContext,
            XalanDOMString&                 value) const
{
    if (m_lettervalue_avt == 0)
    {
        value.clear();
    }
    else
    {
        m_lettervalue_avt->evaluate(
                value,
                *this,
                executionContext);
    }
}



void
ElemNumber::traditionalAlphaCount(
            CountType                               theValue,
            const XalanNumberingResourceBundle&     theResourceBundle,
            XalanDOMString&                         theResult) const
{
    typedef XalanNumberingResourceBundle::NumberTypeVectorType      NumberTypeVectorType;
    typedef XalanNumberingResourceBundle::DigitsTableVectorType DigitsTableVectorType;
    typedef XalanNumberingResourceBundle::eNumberingMethod      eNumberingMethod;
    typedef XalanNumberingResourceBundle::eMultiplierOrder      eMultiplierOrder;

    bool    fError = false;

    // if this number is larger than the largest number we can represent, error!
    //if (val > theResourceBundle.getMaxNumericalValue())
    //return XSLTErrorResources.ERROR_STRING;
    XalanDOMCharVectorType  table(theResult.getMemoryManager());

    // index in table of the last character that we stored
    NumberTypeVectorType::size_type lookupIndex = 1;  // start off with anything other than zero to make correction work

    // Create a buffer to hold the result
    // TODO:  size of the table can be determined by computing
    // logs of the radix.  For now, we fake it.
    XalanDOMChar    buf[100];

    //some languages go left to right(ie. english), right to left (ie. Hebrew),
    //top to bottom (ie.Japanese), etc... Handle them differently
    //String orientation = thisBundle.getString(Constants.LANG_ORIENTATION);

    // next character to set in the buffer
    XalanDOMString::size_type   charPos = 0;

    // array of number groups: ie.1000, 100, 10, 1
    const NumberTypeVectorType& groups = theResourceBundle.getNumberGroups();

    const NumberTypeVectorType::size_type   groupsSize = groups.size();

    // array of tables of hundreds, tens, digits. Indexes into the vectors
    // in the digits table.
    const NumberTypeVectorType& tables = theResourceBundle.getDigitsTableTable();

    const DigitsTableVectorType&    digitsTable = theResourceBundle.getDigitsTable();

    assert(tables.size() == digitsTable.size());

    // some languages have additive alphabetical notation,
    // some multiplicative-additive, etc... Handle them differently.
    const eNumberingMethod  numbering = theResourceBundle.getNumberingMethod();

    // do multiplicative part first
    if (numbering == XalanNumberingResourceBundle::eMultiplicativeAdditive)
    {
        const eMultiplierOrder  mult_order = theResourceBundle.getMultiplierOrder();

        const NumberTypeVectorType& multiplier = theResourceBundle.getMultipliers();

        const NumberTypeVectorType::size_type   multiplierSize = multiplier.size();

        const XalanDOMCharVectorType&   zeroChar = theResourceBundle.getZeroChar();

        const XalanDOMCharVectorType::size_type     zeroCharSize = zeroChar.size();

        const XalanDOMCharVectorType&   multiplierChars = theResourceBundle.getMultiplierChars();

        CountTypeArrayType::size_type   i = 0;

        // skip to correct multiplier
        while (i < multiplierSize && theValue < multiplier[i])
        {
            ++i;
        }

        do
        {
            if (i >= multiplierSize)
            {
                break;            //number is smaller than multipliers
            }

            // some languages (ie chinese) put a zero character (and only one) when
            // the multiplier is multiplied by zero. (ie, 1001 is 1X1000 + 0X100 + 0X10 + 1)
            // 0X100 is replaced by the zero character, we don't need one for 0X10
            if (theValue < multiplier[i])
            {
                if (zeroCharSize == 0)
                {
                    ++i;
                } 
                else
                {
                    if (charPos == 0 ||
                        buf[charPos - 1] != zeroChar[0])
                    {
                        buf[charPos++] = zeroChar[0];
                    }

                    ++i;
                }
            }
            else if (theValue >= multiplier[i])
            {

                const CountType     mult = theValue / multiplier[i];

                theValue = theValue % multiplier[i];         // save this.

                CountTypeArrayType::size_type   k = 0;

                while (k < groupsSize)
                {
                    lookupIndex = 1;                 // initialize for each table
            
                    if (mult / groups[k] <= 0)       // look for right table
                    {
                        k++;
                    }
                    else
                    {
                        assert(digitsTable.size() > DigitsTableVectorType::size_type(tables[k]));

                        // get the table
                        const XalanDOMCharVectorType&   THEletters =
                                digitsTable[tables[k]];

                        const XalanDOMCharVectorType::size_type     THElettersSize =
                            THEletters.size();

                        table.resize(THElettersSize + 1);                   

                        const CountTypeArrayType::size_type tableSize = table.size();

                        XalanDOMCharVectorType::size_type   j = 0;

                        for (; j < THElettersSize; j++)
                        {
                            table[j + 1] = THEletters[j];
                        }

                        table[0] = THEletters[j - 1];    // don't need this

                        // index in "table" of the next char to emit
                        lookupIndex  = mult / groups[k];

                        //this should not happen
                        if (lookupIndex == 0 && mult == 0)
                        {
                            break;
                        }

                        assert(i < multiplierChars.size());

                        const XalanDOMChar  multiplierChar = multiplierChars[i];

                        // put out the next character of output 
                        if (lookupIndex < tableSize)
                        {
                            if(mult_order == XalanNumberingResourceBundle::ePrecedes)
                            {
                                buf[charPos++] = multiplierChar;
                                buf[charPos++] = table[lookupIndex];
                            }
                            else
                            {
                                // don't put out 1 (ie 1X10 is just 10)
                                if (lookupIndex == 1 && i == multiplierSize - 1)
                                {
                                }
                                else
                                {
                                    buf[charPos++] = table[lookupIndex];
                                }

                                buf[charPos++] = multiplierChar;
                            }

                            break;       // all done!
                        }
                        else
                        {
                            fError = true;

                            break;
                        }
                    } //end else
                } // end while  

                i++;

            } // end else if
        } // end do while
        while (i < multiplierSize && fError == false);      
    }

    // Now do additive part...
    CountTypeArrayType::size_type   count = 0;

    // do this for each table of hundreds, tens, digits...
    while (count < groupsSize)
    {
        if (theValue / groups[count] <= 0)
        {
            // look for correct table
            count++;
        }
        else
        {
            const XalanDOMCharVectorType&   theletters =
                                digitsTable[tables[count]];

            const XalanDOMCharVectorType::size_type     thelettersSize =
                            theletters.size();

            table.resize(thelettersSize + 1);
        
            const CountTypeArrayType::size_type tableSize = table.size();

            XalanDOMCharVectorType::size_type   j = 0;

            // need to start filling the table up at index 1
            for (; j < thelettersSize; j++)
            {
                table[j + 1] = theletters[j];
            }

            table[0] = theletters[j - 1];  // don't need this

            // index in "table" of the next char to emit
            lookupIndex  = theValue / groups[count];

            // shift input by one "column"
            theValue = theValue % groups[count];

            // this should not happen
            if (lookupIndex == 0 && theValue == 0)
                break;                  

            if (lookupIndex < tableSize)
            {
                // put out the next character of output 
                buf[charPos++] = table[lookupIndex];    // left to right or top to bottom                   
            }
            else
            {
                fError = true;

                break;
            }

            count++;
        }
    } // end while

    if (fError == true)
    {
        theResult = s_errorString;
    }
    else
    {
        theResult.assign(buf, charPos);
    }
}



const XalanDOMChar  elalphaNumberType = 0x03B1;



void
ElemNumber::getFormattedNumber(
            StylesheetExecutionContext&     executionContext,
            XalanDOMChar                    numberType,
            XalanDOMString::size_type       numberWidth,
            CountType                       listElement,
            XalanDOMString&                 theResult) const
{
    switch(numberType)
    {
        case XalanUnicode::charLetter_A:
            int2alphaCount(listElement, s_alphaCountTable, s_alphaCountTableSize, theResult);
            break;

        case XalanUnicode::charLetter_a:
            {
                int2alphaCount(listElement, s_alphaCountTable, s_alphaCountTableSize, theResult);

                toLowerCaseASCII(theResult);
            }
            break;

        case XalanUnicode::charLetter_I:
            toRoman(listElement, true, theResult);
            break;

        case XalanUnicode::charLetter_i:
            toRoman(listElement, true, theResult);
            toLowerCaseASCII(theResult);
            break;

        case 0x3042:
        case 0x3044:
        case 0x30A2:
        case 0x30A4:
        case 0x4E00:
        case 0x58F9:
        case 0x0E51:
        case 0x05D0:
        case 0x10D0:
        case 0x0430:
            {
                StylesheetExecutionContext::GetCachedString     theGuard(executionContext);

                error(
                    executionContext,
                    XalanMessages::NumberingFormatNotSupported_1Param,
                    NumberToHexDOMString(numberType, theGuard.get()));
                break;
            }

        // Handle the special case of Greek letters for now
        case elalphaNumberType:
            {
                const GetCachedString   theGuard(executionContext);

                XalanDOMString&     letterVal = theGuard.get();

                evaluateLetterValueAVT(executionContext, letterVal);

                if (equals(letterVal, s_traditionalString) == true)
                {
                    traditionalAlphaCount(listElement, s_elalphaResourceBundle, theResult);
                }
                else if (equals(letterVal, s_alphabeticString) == true)
                {
                    int2alphaCount(listElement, s_elalphaCountTable, s_elalphaCountTableSize, theResult);
                }
                else
                {
                    error(
                        executionContext,
                        XalanMessages::LegalValuesForLetterValue);
                }
            }
            break;

        default: // "1"
            {
                StylesheetExecutionContext::XalanNumberFormatAutoPtr    formatter(
                        executionContext.getMemoryManager(),
                        getNumberFormatter(executionContext));

                formatter->format(static_cast<XMLUInt64>(listElement), theResult);

                const XalanDOMString::size_type     lengthNumString = theResult.length();

                if (numberWidth > lengthNumString)
                {
                    const XalanDOMString::size_type nPadding = numberWidth - lengthNumString;

                    const GetCachedString   theGuard(executionContext);

                    XalanDOMString&     padString = theGuard.get();

                    formatter->format(0, padString);

                    theResult.reserve(nPadding * padString.length() + lengthNumString + 1);

                    for (XalanDOMString::size_type i = 0; i < nPadding; i++)
                    {
                        theResult.insert(0, padString);
                    }
                }
            }
            break;
    }
}



void
ElemNumber::int2singlealphaCount(
        CountType               val, 
        const XalanDOMString&   table,
        XalanDOMString&         theResult)
{
    assert(int(table.length()) == table.length());

    const CountType     radix = table.length();

    // TODO:  throw error on out of range input
    if (val > radix)
    {
        theResult = s_errorString;
    }
    else
    {
        const XalanDOMChar  theChar = table[val - 1];

        theResult.assign(1, theChar);
    }
}



void
ElemNumber::int2alphaCount(
            CountType                   val,
            const XalanDOMChar          table[],
            XalanDOMString::size_type   length,
            XalanDOMString&             theResult)
{
    assert(length != 0);

    const CountType     radix = length;

    // Create a buffer to hold the result
    // TODO:  size of the table can be determined by computing
    // logs of the radix.  For now, we fake it.  
    const size_t    buflen = 100;

    XalanDOMChar    buf[buflen + 1];

    std::memset(buf, 0, (buflen + 1) * sizeof(XalanDOMChar));

    // next character to set in the buffer
    XalanDOMString::size_type   charPos = buflen - 1 ;    // work backward through buf[]

    // index in table of the last character that we stored
    size_t  lookupIndex = 1;  // start off with anything other than zero to make correction work
    
    //                      Correction number
    //
    //  Correction can take on exactly two values:
    //
    //      0   if the next character is to be emitted is usual
    //
    //      radix - 1 
    //          if the next char to be emitted should be one less than
    //          you would expect 
    //          
    // For example, consider radix 10, where 1="A" and 10="J"
    //
    // In this scheme, we count: A, B, C ...   H, I, J (not A0 and certainly
    // not AJ), A1
    //
    // So, how do we keep from emitting AJ for 10?  After correctly emitting the
    // J, lookupIndex is zero.  We now compute a correction number of 9 (radix-1).
    // In the following line, we'll compute (val+correction) % radix, which is,
    // (val+9)/10.  By this time, val is 1, so we compute (1+9) % 10, which 
    // is 10 % 10 or zero.  So, we'll prepare to emit "JJ", but then we'll
    // later suppress the leading J as representing zero (in the mod system, 
    // it can represent either 10 or zero).  In summary, the correction value of
    // "radix-1" acts like "-1" when run through the mod operator, but with the 
    // desireable characteristic that it never produces a negative number.

    CountType   correction = 0;

    // TODO:  throw error on out of range input

    do
    {
        // most of the correction calculation is explained above,  the reason for the
        // term after the "|| " is that it correctly propagates carries across
        // multiple columns.  
        correction = ((lookupIndex == 0) || 
            (correction != 0 && lookupIndex == radix - 1 )) ? (radix - 1) : 0;

        // index in "table" of the next char to emit
        lookupIndex  = (val + correction) % radix;  

        // shift input by one "column"
        val = (val / radix);

        // if the next value we'd put out would be a leading zero, we're done.
        if (lookupIndex == 0 && val == 0)
        {
            break;
        }

        // put out the next character of output
        buf[charPos--] = table[lookupIndex];
    }
    while (val > 0);

    theResult.assign(buf + charPos + 1, buflen - charPos - 1);
}



void
ElemNumber::toRoman(
            CountType           val,
            bool                prefixesAreOK,
            XalanDOMString&     theResult)
{
    if(val == 0)
    {
        theResult = XalanUnicode::charDigit_0;
    }
    else if (val > 3999)
    {
        theResult = s_errorString;
    }
    else
    {
        theResult.clear();

        size_t  place = 0;

        DecimalToRoman::ValueType   localValue = val;

        do
        {
            assert(place < s_romanConvertTableSize);

            const DecimalToRoman&   theCurrent =
                s_romanConvertTable[place];

            while (localValue >= theCurrent.m_postValue)
            {
                theResult += theCurrent.m_postLetter;
                localValue -= theCurrent.m_postValue;
            }

            if (prefixesAreOK)
            {
                if (localValue >= theCurrent.m_preValue)
                {
                    theResult += theCurrent.m_preLetter;
                    localValue -= theCurrent.m_preValue;
                }
            } 

            ++place;
        }
        while (localValue > 0);

        assert(localValue == 0);
    }
}



ElemNumber::NumberFormatStringTokenizer::NumberFormatStringTokenizer(
            const XalanDOMString&   theString) :
    m_currentPosition(0),
    m_maxPosition(theString.length()),
    m_string(&theString)
{
}



void
ElemNumber::NumberFormatStringTokenizer::setString(const XalanDOMString&    theString)
{
    m_string = &theString;

    m_currentPosition = 0;
    m_maxPosition = theString.length();
}



void
ElemNumber::NumberFormatStringTokenizer::nextToken(XalanDOMString&  theToken)
{
    if (m_currentPosition >= m_maxPosition) 
    {
        theToken.clear();
    }

    const size_type     start = m_currentPosition;

    if (isXMLLetterOrDigit((*m_string)[m_currentPosition]))
    {
        while (m_currentPosition < m_maxPosition &&
               isXMLLetterOrDigit((*m_string)[m_currentPosition]))
        {
            m_currentPosition++;
        }
    }
    else
    {
        while (m_currentPosition < m_maxPosition &&
               !isXMLLetterOrDigit((*m_string)[m_currentPosition]))
        {
            m_currentPosition++;
        }
    }

    substring(*m_string, theToken, start, m_currentPosition);
}



ElemNumber::NumberFormatStringTokenizer::size_type
ElemNumber::NumberFormatStringTokenizer::countTokens() const
{
    size_type   count = 0;
    size_type   currpos = m_currentPosition;

    // Tokens consist of sequences of alphabetic characters and sequences of
    // non-alphabetic characters
    while (currpos < m_maxPosition) 
    {
        if (isXMLLetterOrDigit((*m_string)[currpos]))
        {
            while (currpos < m_maxPosition &&
                   isXMLLetterOrDigit((*m_string)[currpos]))
            {
                currpos++;
            }
        }
        else
        {
            while (currpos < m_maxPosition &&
                   !isXMLLetterOrDigit((*m_string)[currpos]))
            {
                currpos++;
            }
        }

        count++;
    }

    return count;
}


#define ELEMNUMBER_SIZE(str)    ((sizeof(str) / sizeof(str[0]) - 1))


const XalanDOMChar  ElemNumber::s_alphaCountTable[] =
{
    XalanUnicode::charLetter_Z,
    XalanUnicode::charLetter_A,
    XalanUnicode::charLetter_B,
    XalanUnicode::charLetter_C,
    XalanUnicode::charLetter_D,
    XalanUnicode::charLetter_E,
    XalanUnicode::charLetter_F,
    XalanUnicode::charLetter_G,
    XalanUnicode::charLetter_H,
    XalanUnicode::charLetter_I,
    XalanUnicode::charLetter_J,
    XalanUnicode::charLetter_K,
    XalanUnicode::charLetter_L,
    XalanUnicode::charLetter_M,
    XalanUnicode::charLetter_N,
    XalanUnicode::charLetter_O,
    XalanUnicode::charLetter_P,
    XalanUnicode::charLetter_Q,
    XalanUnicode::charLetter_R,
    XalanUnicode::charLetter_S,
    XalanUnicode::charLetter_T,
    XalanUnicode::charLetter_U,
    XalanUnicode::charLetter_V,
    XalanUnicode::charLetter_W,
    XalanUnicode::charLetter_X,
    XalanUnicode::charLetter_Y,
    0
};

const XalanDOMString::size_type     ElemNumber::s_alphaCountTableSize =
        ELEMNUMBER_SIZE(s_alphaCountTable);



const XalanDOMChar  ElemNumber::s_elalphaCountTable[] =
{
    0x03c9,
    0x03b1,
    0x03b2,
    0x03b3,
    0x03b4,
    0x03b5,
    0x03b6,
    0x03b7,
    0x03b8,
    0x03b9,
    0x03ba,
    0x03bb,
    0x03bc,
    0x03bd,
    0x03be,
    0x03bf,
    0x03c0,
    0x03c1,
    0x03c2,
    0x03c3,
    0x03c4,
    0x03c5,
    0x03c6,
    0x03c7,
    0x03c8,
    0
};


const XalanDOMString::size_type     ElemNumber::s_elalphaCountTableSize =
        ELEMNUMBER_SIZE(s_elalphaCountTable);



static XalanDOMString   s_staticTextString(XalanMemMgrs::getDummyMemMgr());

static XalanDOMString   s_staticCommentString(XalanMemMgrs::getDummyMemMgr());

static XalanDOMString   s_staticSlashString(XalanMemMgrs::getDummyMemMgr());



const XalanDOMChar      ElemNumber::s_atString[] =
{
    XalanUnicode::charAmpersand,
    0
};

const XalanDOMString&   ElemNumber::s_textString = s_staticTextString;

const XalanDOMString&   ElemNumber::s_commentString = s_staticCommentString;

const XalanDOMString&   ElemNumber::s_slashString = s_staticSlashString;

const XalanDOMChar      ElemNumber::s_piString[] =
{
    XalanUnicode::charLetter_p,
    XalanUnicode::charLetter_r,
    XalanUnicode::charLetter_o,
    XalanUnicode::charLetter_c,
    XalanUnicode::charLetter_e,
    XalanUnicode::charLetter_s,
    XalanUnicode::charLetter_s,
    XalanUnicode::charLetter_i,
    XalanUnicode::charLetter_n,
    XalanUnicode::charLetter_g,
    XalanUnicode::charHyphenMinus,
    XalanUnicode::charLetter_i,
    XalanUnicode::charLetter_n,
    XalanUnicode::charLetter_s,
    XalanUnicode::charLetter_t,
    XalanUnicode::charLetter_r,
    XalanUnicode::charLetter_u,
    XalanUnicode::charLetter_c,
    XalanUnicode::charLetter_t,
    XalanUnicode::charLetter_i,
    XalanUnicode::charLetter_o,
    XalanUnicode::charLetter_n,
    XalanUnicode::charLeftParenthesis,
    0
};

const XalanDOMChar      ElemNumber::s_levelString[] =
{
    XalanUnicode::charLetter_l,
    XalanUnicode::charLetter_e,
    XalanUnicode::charLetter_v,
    XalanUnicode::charLetter_e,
    XalanUnicode::charLetter_l,
    0
};

const XalanDOMChar      ElemNumber::s_multipleString[] =
{
    XalanUnicode::charLetter_m,
    XalanUnicode::charLetter_u,
    XalanUnicode::charLetter_l,
    XalanUnicode::charLetter_t,
    XalanUnicode::charLetter_i,
    XalanUnicode::charLetter_p,
    XalanUnicode::charLetter_l,
    XalanUnicode::charLetter_e,
    0
};

const XalanDOMChar      ElemNumber::s_anyString[] =
{
    XalanUnicode::charLetter_a,
    XalanUnicode::charLetter_n,
    XalanUnicode::charLetter_y,
    0
};

const XalanDOMChar      ElemNumber::s_singleString[] =
{
    XalanUnicode::charLetter_s,
    XalanUnicode::charLetter_i,
    XalanUnicode::charLetter_n,
    XalanUnicode::charLetter_g,
    XalanUnicode::charLetter_l,
    XalanUnicode::charLetter_e,
    0
};

const XalanDOMChar      ElemNumber::s_alphabeticString[] =
{
    XalanUnicode::charLetter_a,
    XalanUnicode::charLetter_l,
    XalanUnicode::charLetter_p,
    XalanUnicode::charLetter_h,
    XalanUnicode::charLetter_a,
    XalanUnicode::charLetter_b,
    XalanUnicode::charLetter_e,
    XalanUnicode::charLetter_t,
    XalanUnicode::charLetter_i,
    XalanUnicode::charLetter_c,
    0
};

const XalanDOMChar      ElemNumber::s_traditionalString[] =
{
    XalanUnicode::charLetter_t,
    XalanUnicode::charLetter_r,
    XalanUnicode::charLetter_a,
    XalanUnicode::charLetter_d,
    XalanUnicode::charLetter_i,
    XalanUnicode::charLetter_t,
    XalanUnicode::charLetter_i,
    XalanUnicode::charLetter_o,
    XalanUnicode::charLetter_n,
    XalanUnicode::charLetter_a,
    XalanUnicode::charLetter_l,
    0
};



const XalanDOMChar      ElemNumber::s_errorString[] =
{
    XalanUnicode::charNumberSign,
    XalanUnicode::charLetter_e,
    XalanUnicode::charLetter_r,
    XalanUnicode::charLetter_r,
    XalanUnicode::charLetter_o,
    XalanUnicode::charLetter_r,
    0
};



const DecimalToRoman    ElemNumber::s_romanConvertTable[] =
{    {
        1000,
        { XalanUnicode::charLetter_M, 0 },
        900,
        { XalanUnicode::charLetter_C, XalanUnicode::charLetter_M, 0 }
    },

    {
        500,
        { XalanUnicode::charLetter_D, 0 },
        400,
        { XalanUnicode::charLetter_C, XalanUnicode::charLetter_D, 0 }
    },

    {
        100,
        { XalanUnicode::charLetter_C, 0 },
        90,
        { XalanUnicode::charLetter_X, XalanUnicode::charLetter_C, 0 }
    },

    {
        50,
        { XalanUnicode::charLetter_L, 0 },
        40,
        { XalanUnicode::charLetter_X, XalanUnicode::charLetter_L, 0 }
    },

    {
        10,
        { XalanUnicode::charLetter_X, 0 },
        9,
        { XalanUnicode::charLetter_I, XalanUnicode::charLetter_X, 0 }
    },

    {
        5,
        { XalanUnicode::charLetter_V, 0 },
        4,
        { XalanUnicode::charLetter_I, XalanUnicode::charLetter_V, 0 }
    },

    {
        1,
        { XalanUnicode::charLetter_I, 0 },
        1,
        { XalanUnicode::charLetter_I, 0 }
    }
};


const size_t    ElemNumber::s_romanConvertTableSize = sizeof(s_romanConvertTable) / sizeof(s_romanConvertTable[0]);


static XalanNumberingResourceBundle     s_staticElalphaResourceBundle(XalanMemMgrs::getDummyMemMgr());

const XalanNumberingResourceBundle& ElemNumber::s_elalphaResourceBundle =
        s_staticElalphaResourceBundle;



static void
initializeTraditionalElalphaBundle(
            MemoryManager&              theManager, 
            XalanNumberingResourceBundle&   theBundle)
{


    // The following are a bunch of static data the comprise the contents of the bundle.
    static const XalanDOMChar   elalphaAlphabet[] =
    {
        0x03b1, 0x03b2, 0x03b3, 0x03b4, 0x03b5, 0x03b6, 0x03b7,  0x03b8,
        0x03b9, 0x03ba, 0x03bb, 0x03bc, 0x03bd, 0x03be, 0x03bf, 0x03c0,
        0x03c1, 0x03c2, 0x03c3, 0x03c4, 0x03c5, 0x03c6, 0x03c7, 0x03c8,
        0x03c9, 0
    };

    static const XalanDOMChar   elalphaTraditionalAlphabet[] =
    {
        XalanUnicode::charLetter_A, XalanUnicode::charLetter_B, XalanUnicode::charLetter_C,
        XalanUnicode::charLetter_D, XalanUnicode::charLetter_E, XalanUnicode::charLetter_F,
        XalanUnicode::charLetter_G, XalanUnicode::charLetter_H, XalanUnicode::charLetter_I,
        XalanUnicode::charLetter_J, XalanUnicode::charLetter_K, XalanUnicode::charLetter_L,
        XalanUnicode::charLetter_M, XalanUnicode::charLetter_N, XalanUnicode::charLetter_O,
        XalanUnicode::charLetter_P, XalanUnicode::charLetter_Q, XalanUnicode::charLetter_R,
        XalanUnicode::charLetter_S, XalanUnicode::charLetter_T, XalanUnicode::charLetter_U,
        XalanUnicode::charLetter_V, XalanUnicode::charLetter_W, XalanUnicode::charLetter_X,
        XalanUnicode::charLetter_Y, XalanUnicode::charLetter_Z, 0
    };

    typedef XalanNumberingResourceBundle::NumberType    NumberType;

    static const NumberType     elalphaNumberGroups[] =
    {
        100, 10, 1
    };

    const size_t    elalphaNumberGroupsCount = sizeof(elalphaNumberGroups) / sizeof(elalphaNumberGroups[0]);

    static const NumberType     elalphaMultipliers[] = { 1000 };

    const size_t    elalphaMultipliersCount = sizeof(elalphaMultipliers) / sizeof(elalphaMultipliers[0]);

    static const XalanDOMChar   elalphaMultiplierChars[] = { 0x03d9, 0 };

    static const XalanDOMChar   elalphaDigits[] =
    {
        0x03b1, 0x03b2, 0x03b3, 0x03b4, 0x03b5, 0x03db, 0x03b6, 0x03b7, 0x03b8, 0
    };

    static const XalanDOMChar   elalphaTens[] =
    {
        0x03b9, 0x03ba, 0x03bb, 0x03bc, 0x03bd, 0x03be, 0x03bf, 0x03c0, 0x03df, 0
    };

    static const XalanDOMChar   elalphaHundreds[] =
    {
        0x03c1, 0x03c2, 0x03c4, 0x03c5, 0x03c6, 0x03c7, 0x03c8, 0x03c9, 0x03e1, 0
    };

    typedef XalanNumberingResourceBundle::DigitsTableVectorType     DigitsTableVectorType;
    typedef XalanNumberingResourceBundle::NumberTypeVectorType          NumberTypeVectorType;

    // Create the table of characters for the various digit positions...
    DigitsTableVectorType           theElalphaDigitsTable(theManager);

    // Since we know the size, create the empty vectors in the table...
    theElalphaDigitsTable.resize(3);

    // Swap the empty tables with temporary ones...
    XalanDOMCharVectorType(
        elalphaDigits,
        elalphaDigits + length(elalphaDigits),
        theManager).swap(theElalphaDigitsTable[0]);

    XalanDOMCharVectorType(
        elalphaTens,
        elalphaTens + length(elalphaTens),
        theManager).swap(theElalphaDigitsTable[1]);

    XalanDOMCharVectorType(
        elalphaHundreds,
        elalphaHundreds + length(elalphaHundreds),
        theManager).swap(theElalphaDigitsTable[2]);

    // This table will indicate which positions the vectors of digits are in
    // the table...
    NumberTypeVectorType    theDigitsTableTable(theManager);

    theDigitsTableTable.reserve(3);

    // Push the positions on in reverse order, since that's how things work...
    theDigitsTableTable.push_back(2);
    theDigitsTableTable.push_back(1);
    theDigitsTableTable.push_back(0);

    const XalanDOMString    theLanguageString("el", theManager);

    // Create an instance...
    XalanNumberingResourceBundle    theElaphaBundle(
        theLanguageString,
        theLanguageString,
        theLanguageString,
        XalanDOMCharVectorType(
            elalphaAlphabet,
            elalphaAlphabet + length(elalphaAlphabet),
            theManager),
        XalanDOMCharVectorType(
            elalphaTraditionalAlphabet,
            elalphaTraditionalAlphabet + length(elalphaTraditionalAlphabet),
            theManager),
        XalanNumberingResourceBundle::eLeftToRight,
        XalanNumberingResourceBundle::eMultiplicativeAdditive,
        XalanNumberingResourceBundle::ePrecedes,
        ~NumberType(0),
        NumberTypeVectorType(
            elalphaNumberGroups,
            elalphaNumberGroups + elalphaNumberGroupsCount,
            theManager),
        NumberTypeVectorType(
            elalphaMultipliers,
            elalphaMultipliers + elalphaMultipliersCount,
            theManager),
        XalanDOMCharVectorType(theManager),
        XalanDOMCharVectorType(
            elalphaMultiplierChars,
            elalphaMultiplierChars + length(elalphaMultiplierChars),
            theManager),
        theElalphaDigitsTable,
        theDigitsTableTable,
        theManager);

    // Swap it with the one (this avoids making a copy...)
    theBundle.swap(theElaphaBundle);
}



static const XalanDOMChar   s_textStringChars[] =
{
    XalanUnicode::charLetter_t,
    XalanUnicode::charLetter_e,
    XalanUnicode::charLetter_x,
    XalanUnicode::charLetter_t,
    XalanUnicode::charLeftParenthesis,
    XalanUnicode::charRightParenthesis,
    0
};



static const XalanDOMChar   s_commentStringChars[] =
{
    XalanUnicode::charLetter_c,
    XalanUnicode::charLetter_o,
    XalanUnicode::charLetter_m,
    XalanUnicode::charLetter_m,
    XalanUnicode::charLetter_e,
    XalanUnicode::charLetter_n,
    XalanUnicode::charLetter_t,
    XalanUnicode::charLeftParenthesis,
    XalanUnicode::charRightParenthesis,
    0
};



static const XalanDOMChar   s_solidusStringChars[] =
{
    XalanUnicode::charSolidus,
    0
};



void
ElemNumber::initialize(MemoryManager&  theManager)
{
    s_staticTextString.reset(theManager, s_textStringChars);

    s_staticCommentString.reset(theManager, s_commentStringChars);

    s_staticSlashString.reset(theManager, s_solidusStringChars);

    initializeTraditionalElalphaBundle(theManager, s_staticElalphaResourceBundle);
}



void
ElemNumber::terminate()
{
    MemoryManager& theManager = XalanMemMgrs::getDummyMemMgr();

    releaseMemory(s_staticTextString, theManager);
    releaseMemory(s_staticCommentString, theManager);
    releaseMemory(s_staticSlashString, theManager);

    XalanNumberingResourceBundle(theManager).swap(s_staticElalphaResourceBundle);
}



const XPath*
ElemNumber::getXPath(XalanSize_t    index) const
{
    const XPath*    result = 0;

    switch(index)
    {
    case 0:
        result = m_valueExpr;
        break;

    case 1:
        result = m_countMatchPattern;
        break;

    case 2:
        result = m_fromMatchPattern;
        break;

    default:
        break;
    }

    return result;
}



}
