/*
 * 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.
 */

/*
 * $Id: RangeToken.cpp 568078 2007-08-21 11:43:25Z amassari $
 */

// ---------------------------------------------------------------------------
//  Includes
// ---------------------------------------------------------------------------
#include <assert.h>

#include <xercesc/util/regx/RangeToken.hpp>
#include <xercesc/util/regx/TokenFactory.hpp>
#include <xercesc/util/IllegalArgumentException.hpp>

#if defined(XML_USE_ICU_TRANSCODER) || defined (XML_USE_UNICONV390_TRANSCODER)
#include <unicode/uchar.h>
#else
#include <xercesc/util/XMLUniDefs.hpp>
#endif

XERCES_CPP_NAMESPACE_BEGIN

// ---------------------------------------------------------------------------
//  Static member data initialization
// ---------------------------------------------------------------------------
const int RangeToken::MAPSIZE = 256;
const unsigned int RangeToken::INITIALSIZE = 16;

// ---------------------------------------------------------------------------
//  RangeToken: Constructors and Destructors
// ---------------------------------------------------------------------------
RangeToken::RangeToken(const unsigned short tokType,
                       MemoryManager* const manager) 
    : Token(tokType, manager)
    , fSorted(false)
    , fCompacted(false)
    , fNonMapIndex(0)
    , fElemCount(0)
    , fMaxCount(INITIALSIZE)
    , fMap(0)
    , fRanges(0)
    , fCaseIToken(0)
    , fMemoryManager(manager)
{

}

RangeToken::~RangeToken() {

    fMemoryManager->deallocate(fMap);//delete [] fMap;
    fMemoryManager->deallocate(fRanges);//delete[] fRanges;
}


// This is a struct that defines a mapping for
// case-insensitive matching.  The first character
// is the character we try to match in the range.
// The second is the character we add to the range,
// because it maps to the first when we're folding
// case.
struct ExceptionCharsStruct
{
    XMLInt32    baseChar;

    XMLInt32    matchingChar;
};


// This is an array of character mappings that we will
// add to ranges for case-insensitive matching.
static const ExceptionCharsStruct   s_exceptions[] =
{
    { 0x49, 0x130 },
    { 0x49, 0x131 },
    { 0x4b, 0x212a },
    { 0x53, 0x17f },
    { 0x69, 0x130 },
    { 0x69, 0x131 },
    { 0x6b, 0x212a },
    { 0x73, 0x17f },
    { 0xc5, 0x212b },
    { 0xe5, 0x212b },
    { 0x1c4, 0x1c5 },
    { 0x1c6, 0x1c5 },
    { 0x1c7, 0x1c8 },
    { 0x1c9, 0x1c8 },
    { 0x1ca, 0x1cb },
    { 0x1cc, 0x1cb },
    { 0x1f1, 0x1f2 },
    { 0x1f3, 0x1f2 },
    { 0x392, 0x3d0 },
    { 0x395, 0x3f5 },
    { 0x398, 0x3d1 },
    { 0x398, 0x3f4 },
    { 0x399, 0x345 },
    { 0x399, 0x1fbe },
    { 0x39a, 0x3f0 },
    { 0x39c, 0xb5 },
    { 0x3a0, 0x3d6 },
    { 0x3a1, 0x3f1 },
    { 0x3a3, 0x3c2 },
    { 0x3a6, 0x3d5 },
    { 0x3a9, 0x2126 },
    { 0x3b2, 0x3d0 },
    { 0x3b5, 0x3f5 },
    { 0x3b8, 0x3d1 },
    { 0x3b8, 0x3f4 },
    { 0x3b9, 0x345 },
    { 0x3b9, 0x1fbe },
    { 0x3ba, 0x3f0 },
    { 0x3bc, 0xb5 },
    { 0x3c0, 0x3d6 },
    { 0x3c1, 0x3f1 },
    { 0x3c3, 0x3c2 },
    { 0x3c6, 0x3d5 },
    { 0x3c9, 0x2126 },
    { 0x1e60, 0x1e9b },
    { 0x1e61, 0x1e9b }
};

// ---------------------------------------------------------------------------
//  RangeToken: Getter methods
// ---------------------------------------------------------------------------
RangeToken* RangeToken::getCaseInsensitiveToken(TokenFactory* const tokFactory) {

    if (fCaseIToken == 0 && tokFactory && fRanges) {

        bool isNRange = (getTokenType() == T_NRANGE) ? true : false;
        RangeToken* lwrToken = tokFactory->createRange(isNRange);

        unsigned int exceptIndex = 0;

        for (unsigned int i = 0;  i < fElemCount - 1;  i += 2) {
            for (XMLInt32 ch = fRanges[i];  ch <= fRanges[i + 1];  ++ch) {
#if defined(XML_USE_ICU_TRANSCODER) || defined (XML_USE_UNICONV390_TRANSCODER)
                const XMLInt32  upperCh = u_toupper(ch);

                if (upperCh != ch)
                {
                    lwrToken->addRange(upperCh, upperCh);
                }

                const XMLInt32  lowerCh = u_tolower(ch);

                if (lowerCh != ch)
                {
                    lwrToken->addRange(lowerCh, lowerCh);
                }

                const XMLInt32  titleCh = u_totitle(ch);

                if (titleCh != ch && titleCh != upperCh)
                {
                    lwrToken->addRange(titleCh, titleCh);
                }
#else
                if (ch >= chLatin_A && ch <= chLatin_Z)
                {
                    ch += chLatin_a - chLatin_A;

                    lwrToken->addRange(ch, ch);
                }
                else if (ch >= chLatin_a && ch <= chLatin_z)
                {
                    ch -= chLatin_a - chLatin_A;

                    lwrToken->addRange(ch, ch);
                }
#endif

                const unsigned int  exceptionsSize =
                    sizeof(s_exceptions) / sizeof(s_exceptions[0]);

                // Add any exception chars.  These are characters where the the
                // case mapping is not symmetric.  (Unicode case mappings are not isomorphic...)
                while (exceptIndex < exceptionsSize)
                {
                    if (s_exceptions[exceptIndex].baseChar < ch)
                    {
                        ++exceptIndex;
                    }
                    else if (s_exceptions[exceptIndex].baseChar == ch)
                    {
                        const XMLInt32  matchingChar =
                            s_exceptions[exceptIndex].matchingChar;

                        lwrToken->addRange(
                            matchingChar,
                            matchingChar);

                        ++exceptIndex;
                    }
                    else
                    {
                        break;
                    }
                }
            }
        }

        lwrToken->mergeRanges(this);
        lwrToken->compactRanges();
        lwrToken->createMap();

        fCaseIToken = lwrToken;
    }

    return fCaseIToken;
}

// ---------------------------------------------------------------------------
//  RangeToken: Setter methods
// ---------------------------------------------------------------------------
void RangeToken::setRangeValues(XMLInt32* const rangeValues, const unsigned int count)
{
    if (fRanges) {

        if (fMap) {
            fMemoryManager->deallocate(fMap);//delete [] fMap;
            fMap = 0;
        }

        fElemCount = 0;
        fMemoryManager->deallocate(fRanges);//delete [] fRanges;
        fRanges = 0;
    }

    fElemCount = fMaxCount = count;
    fRanges = rangeValues;
}

// ---------------------------------------------------------------------------
//  RangeToken: Range manipulation methods
// ---------------------------------------------------------------------------
void RangeToken::addRange(const XMLInt32 start, const XMLInt32 end) {

    XMLInt32 val1, val2;

    fCaseIToken = 0;

    if (start <= end) {

        val1 = start;
        val2 = end;
    }
    else {

        val1 = end;
        val2 = start;
    }

    if (fRanges == 0) {

        fRanges = (XMLInt32*) fMemoryManager->allocate
        (
            fMaxCount * sizeof(XMLInt32)
        );//new XMLInt32[fMaxCount];
        fRanges[0] = val1;
        fRanges[1] = val2;
        fElemCount = 2;
        fSorted = true;
    }
    else {

        if (fRanges[fElemCount-1] + 1 == val1) {

            fRanges[fElemCount-1] = val2;
            return;
        }

        if (fElemCount + 2 >= fMaxCount) {
            expand(2);
        }

        if(fSorted && fRanges[fElemCount-1] >= val1)
        {
            for (int i = 0; i < (int)fElemCount; i +=2)
            {
                // check if this range is already part of this one
                if (fRanges[i] <= val1 && fRanges[i+1] >= val2)
                    break;
                // or if the new one extends the old one
                else if(fRanges[i]==val1 && fRanges[i+1] < val2)
                {
                    fRanges[i+1]=val2;
                    break;
                }
                else if (fRanges[i] > val1 ||
                          (fRanges[i]==val1 && fRanges[i+1] > val2))
                {
                    for(int j=fElemCount-1;j>=i;j--)
                        fRanges[j+2]=fRanges[j];
                    fRanges[i]   = val1;
                    fRanges[i+1] = val2;
                    fElemCount  += 2;
                    break;
                }
            }
        }
        else
        {
            if (fRanges[fElemCount-1] >= val1)
                fSorted = false;

            fRanges[fElemCount++] = val1;
            fRanges[fElemCount++] = val2;

            if (!fSorted) {
                sortRanges();
            }
        }
    }
}

void RangeToken::sortRanges() {

    if (fSorted || fRanges == 0)
        return;

    for (int i = fElemCount - 4; i >= 0; i -= 2) {

        for (int j = 0; j <= i; j +=2) {

            if (fRanges[j] > fRanges[j + 2]
                || (fRanges[j]==fRanges[j+2] && fRanges[j+1] > fRanges[j+3])) {

                XMLInt32 tmpVal = fRanges[j+2];
                fRanges[j+2] = fRanges[j];
                fRanges[j] = tmpVal;
                tmpVal = fRanges[j+3];
                fRanges[j+3] = fRanges[j+1];
                fRanges[j+1] = tmpVal;
            }
        }
    }

    fSorted = true;
}

void RangeToken::compactRanges() {

    if (fCompacted || fRanges == 0 || fElemCount <= 2)
        return;

    unsigned int base = 0;
    unsigned int target = 0;

    while (target < fElemCount) {

        if (base != target) {

            fRanges[base] = fRanges[target++];
            fRanges[base+1] = fRanges[target++];
        }
        else
            target += 2;

        XMLInt32 baseEnd = fRanges[base + 1];

        while (target < fElemCount) {

            XMLInt32 startRange = fRanges[target];

            if (baseEnd + 1 < startRange)
                break;

            XMLInt32 endRange = fRanges[target + 1];

            if (baseEnd + 1 == startRange || baseEnd < endRange) {

                baseEnd = endRange;
                fRanges[base+1] = baseEnd;
                target += 2;
            }
            else if (baseEnd >= endRange) {
                target += 2;
            }
            else {
                ThrowXMLwithMemMgr(RuntimeException, XMLExcepts::Regex_CompactRangesError, fMemoryManager);
            }
        } // inner while

        base += 2;
    }

    fElemCount = base;
    fCompacted = true;
}

void RangeToken::mergeRanges(const Token *const tok) {


    if (tok->getTokenType() != this->getTokenType())
        ThrowXMLwithMemMgr(IllegalArgumentException, XMLExcepts::Regex_MergeRangesTypeMismatch, fMemoryManager);

    RangeToken* rangeTok = (RangeToken *) tok;

    if (rangeTok->fRanges == 0)
        return;

    fCaseIToken = 0;
    sortRanges();
    rangeTok->sortRanges();

    if (fRanges == 0) {

        fMaxCount = rangeTok->fMaxCount;
        fRanges = (XMLInt32*) fMemoryManager->allocate
        (
            fMaxCount * sizeof(XMLInt32)
        );//new XMLInt32[fMaxCount];
        for (unsigned int index = 0; index < rangeTok->fElemCount; index++) {
            fRanges[index] = rangeTok->fRanges[index];
        }

        fElemCount = rangeTok->fElemCount;
        fSorted = true;
        return;
    }

    unsigned int newMaxCount = (fElemCount + rangeTok->fElemCount >= fMaxCount)
                                 ? fMaxCount + rangeTok->fMaxCount : fMaxCount;
    XMLInt32* result = (XMLInt32*) fMemoryManager->allocate
    (
        newMaxCount * sizeof(XMLInt32)
    );//new XMLInt32[newMaxCount];

    for (unsigned int i=0, j=0, k=0; i < fElemCount || j < rangeTok->fElemCount;) {

        if (i >= fElemCount) {

            for (int count = 0; count < 2; count++) {
                result[k++] = rangeTok->fRanges[j++];
            }
        }
        else if (j >= rangeTok->fElemCount) {

            for (int count = 0; count < 2; count++) {
                result[k++] = fRanges[i++];
            }
        }
        else if (rangeTok->fRanges[j] < fRanges[i]
                 || (rangeTok->fRanges[j] == fRanges[i]
                     && rangeTok->fRanges[j+1] < fRanges[i+1])) {

            for (int count = 0; count < 2; count++) {
                result[k++] = rangeTok->fRanges[j++];
            }
        }
        else {

            for (int count = 0; count < 2; count++) {

                result[k++] = fRanges[i++];
            }
        }
    }

    fMemoryManager->deallocate(fRanges);//delete [] fRanges;
    fElemCount += rangeTok->fElemCount;
    fRanges = result;
    fMaxCount = newMaxCount;
}

void RangeToken::subtractRanges(RangeToken* const tok) {

    if (fRanges == 0 || tok->fRanges == 0)
        return;

    if (tok->getTokenType() == T_NRANGE) {

        intersectRanges(tok);
        return;
    }

    fCaseIToken = 0;
    sortRanges();
    compactRanges();
    tok->sortRanges();
    tok->compactRanges();

    unsigned int newMax = (fElemCount + tok->fElemCount >= fMaxCount)
                             ? fMaxCount + tok->fMaxCount : fMaxCount;
    XMLInt32* result = (XMLInt32*) fMemoryManager->allocate
    (
        newMax * sizeof(XMLInt32)
    );//new XMLInt32[newMax];
    unsigned int newElemCount = 0;
    unsigned int srcCount = 0;
    unsigned int subCount = 0;

    while (srcCount < fElemCount && subCount < tok->fElemCount) {

        XMLInt32 srcBegin = fRanges[srcCount];
        XMLInt32 srcEnd = fRanges[srcCount + 1];
        XMLInt32 subBegin = tok->fRanges[subCount];
        XMLInt32 subEnd = tok->fRanges[subCount + 1];

        if (srcEnd < subBegin) { // no overlap

            result[newElemCount++] = fRanges[srcCount++];
            result[newElemCount++] = fRanges[srcCount++];
        }
        else if (srcEnd >= subBegin && srcBegin <= subEnd) {

            if (subBegin <= srcBegin && srcEnd <= subEnd) {
                srcCount += 2;
            }
            else if (subBegin <= srcBegin) {

                fRanges[srcCount] = subEnd + 1;
                subCount += 2;
            }
            else if (srcEnd <= subEnd) {

                result[newElemCount++] = srcBegin;
                result[newElemCount++] = subBegin - 1;
                srcCount += 2;
            }
            else {

                result[newElemCount++] = srcBegin;
                result[newElemCount++] = subBegin - 1;
                fRanges[srcCount] = subEnd + 1;
                subCount += 2;
            }
        }
        else if (subEnd < srcBegin) {
            subCount += 2;
        }
        else {
            fMemoryManager->deallocate(result);//delete [] result;
            ThrowXMLwithMemMgr(RuntimeException, XMLExcepts::Regex_SubtractRangesError, fMemoryManager);
        }
    } //end while

    while (srcCount < fElemCount) {

        result[newElemCount++] = fRanges[srcCount++];
        result[newElemCount++] = fRanges[srcCount++];
    }

    fMemoryManager->deallocate(fRanges);//delete [] fRanges;
    fRanges = result;
    fElemCount = newElemCount;
    fMaxCount = newMax;
}

/**
  * Ignore whether 'tok' is NRANGE or not.
  */
void RangeToken::intersectRanges(RangeToken* const tok) {

    if (fRanges == 0 || tok->fRanges == 0)
        return;

    fCaseIToken = 0;
    sortRanges();
    compactRanges();
    tok->sortRanges();
    tok->compactRanges();

    unsigned int newMax = (fElemCount + tok->fElemCount >= fMaxCount)
                             ? fMaxCount + tok->fMaxCount : fMaxCount;
    XMLInt32* result = (XMLInt32*) fMemoryManager->allocate
    (
        newMax * sizeof(XMLInt32)
    );//new XMLInt32[newMax];
    unsigned int newElemCount = 0;
    unsigned int srcCount = 0;
    unsigned int tokCount = 0;

    while (srcCount < fElemCount && tokCount < tok->fElemCount) {

        XMLInt32 srcBegin = fRanges[srcCount];
        XMLInt32 srcEnd = fRanges[srcCount + 1];
        XMLInt32 tokBegin = tok->fRanges[tokCount];
        XMLInt32 tokEnd = tok->fRanges[tokCount + 1];

        if (srcEnd < tokBegin) {
            srcCount += 2;
        }
        else if (srcEnd >= tokBegin && srcBegin <= tokEnd) {

            if (tokBegin <= srcBegin && srcEnd <= tokEnd) {

                result[newElemCount++] = srcBegin;
                result[newElemCount++] = srcEnd;
                srcCount += 2;
            }
            else if (tokBegin <= srcBegin) {

                result[newElemCount++] = srcBegin;
                result[newElemCount++] = tokEnd;
                tokCount += 2;

                if (tokCount < tok->fElemCount)
                    fRanges[srcCount] = tokEnd + 1;
                else
                    srcCount += 2;
            }
            else if (srcEnd <= tokEnd) {

                result[newElemCount++] = tokBegin;
                result[newElemCount++] = srcEnd;
                srcCount += 2;
            }
            else {

                result[newElemCount++] = tokBegin;
                result[newElemCount++] = tokEnd;
                tokCount += 2;

                if (tokCount < tok->fElemCount)
                    fRanges[srcCount] = tokEnd + 1;
                else
                    srcCount += 2;
            }
        }
        else if (tokEnd < srcBegin) {
            tokCount += 2;

            if (tokCount >= tok->fElemCount)
                srcCount += 2;
        }
        else {

            fMemoryManager->deallocate(result);//delete [] result;
            ThrowXMLwithMemMgr(RuntimeException, XMLExcepts::Regex_IntersectRangesError, fMemoryManager);
        }
    } //end while

    fMemoryManager->deallocate(fRanges);//delete [] fRanges;
    fRanges = result;
    fElemCount = newElemCount;
    fMaxCount = newMax;
}

/**
  * for RANGE: Creates complement.
  * for NRANGE: Creates the same meaning RANGE.
  */
Token* RangeToken::complementRanges(RangeToken* const tok,
                                    TokenFactory* const tokFactory,
                                    MemoryManager* const manager) {

    if (tok->getTokenType() != T_RANGE && tok->getTokenType() != T_NRANGE)
        ThrowXMLwithMemMgr(IllegalArgumentException, XMLExcepts::Regex_ComplementRangesInvalidArg, manager);

    tok->sortRanges();
    tok->compactRanges();

    XMLInt32 lastElem = tok->fRanges[tok->fElemCount - 1];
    RangeToken* rangeTok = tokFactory->createRange();

    if (tok->fRanges[0] > 0) {
        rangeTok->addRange(0, tok->fRanges[0] - 1);
    }

    for (unsigned int i= 1; i< tok->fElemCount - 2; i += 2) {
        rangeTok->addRange(tok->fRanges[i] + 1, tok->fRanges[i+1] - 1);
    }

    if (lastElem != UTF16_MAX) {
        rangeTok->addRange(lastElem + 1, UTF16_MAX);
    }

    rangeTok->fCompacted = true;

    return rangeTok;
}


// ---------------------------------------------------------------------------
//  RangeToken: Match methods
// ---------------------------------------------------------------------------
bool RangeToken::match(const XMLInt32 ch) {

    createMap();

    bool ret;

    if (getTokenType() == T_RANGE) {

        if (ch < MAPSIZE)
            return ((fMap[ch/32] & (1<<(ch&0x1f))) != 0);

        ret = false;

        for (unsigned int i= fNonMapIndex; i< fElemCount; i +=2) {

            if (fRanges[i] <= ch && ch <= fRanges[i+1])
                return true;
        }
    }
    else {

        if (ch < MAPSIZE)
            return ((fMap[ch/32] & (1<<(ch&0x1f))) == 0);

        ret = true;

        for (unsigned int i= fNonMapIndex; i< fElemCount; i += 2) {

            if (fRanges[i] <= ch && ch <= fRanges[i+1])
                return false;
        }
    }

    return ret;
}

// ---------------------------------------------------------------------------
//  RangeToken: Private helpers methods
// ---------------------------------------------------------------------------
void RangeToken::expand(const unsigned int length) {

    unsigned int newMax = fElemCount + length;

    // Avoid too many reallocations by expanding by a percentage
    unsigned int minNewMax = (unsigned int)((double)fElemCount * 1.25);
    if (newMax < minNewMax)
        newMax = minNewMax;

    XMLInt32* newList = (XMLInt32*) fMemoryManager->allocate
    (
        newMax * sizeof(XMLInt32)
    );//new XMLInt32[newMax];
    for (unsigned int index = 0; index < fElemCount; index++)
        newList[index] = fRanges[index];

    fMemoryManager->deallocate(fRanges);//delete [] fRanges;
    fRanges = newList;
    fMaxCount = newMax;
}

void RangeToken::doCreateMap() {

    assert(!fMap);

    int asize = MAPSIZE/32;
    fMap = (int*) fMemoryManager->allocate(asize * sizeof(int));//new int[asize];
    fNonMapIndex = fElemCount;

    for (int i = 0; i < asize; i++) {
        fMap[i] = 0;
    }

    for (unsigned int j= 0; j < fElemCount; j += 2) {

        XMLInt32 begin = fRanges[j];
        XMLInt32 end = fRanges[j+1];

        if (begin < MAPSIZE) {

            for (int k = begin; k <= end && k < MAPSIZE; k++) {
                fMap[k/32] |= 1<<(k&0x1F);
            }
        }
        else {

            fNonMapIndex = j;
            break;
        }

        if (end >= MAPSIZE) {

            fNonMapIndex = j;
            break;
        }
    }
}

XERCES_CPP_NAMESPACE_END

/**
  * End of file RangeToken.cpp
  */
