/*
 * The Apache Software License, Version 1.1
 *
 * Copyright (c) 2001 The Apache Software Foundation.  All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution,
 *    if any, must include the following acknowledgment:
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowledgment may appear in the software itself,
 *    if and wherever such third-party acknowledgments normally appear.
 *
 * 4. The names "Xerces" and "Apache Software Foundation" must
 *    not be used to endorse or promote products derived from this
 *    software without prior written permission. For written
 *    permission, please contact apache\@apache.org.
 *
 * 5. Products derived from this software may not be called "Apache",
 *    nor may "Apache" appear in their name, without prior written
 *    permission of the Apache Software Foundation.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation, and was
 * originally based on software copyright (c) 2001, International
 * Business Machines, Inc., http://www.ibm.com .  For more information
 * on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 */

/*
 * $Id: ListDatatypeValidator.cpp,v 1.13 2003/11/28 18:53:07 peiyongz Exp $
 * $Log: ListDatatypeValidator.cpp,v $
 * Revision 1.13  2003/11/28 18:53:07  peiyongz
 * Support for getCanonicalRepresentation
 *
 * Revision 1.12  2003/11/12 20:32:03  peiyongz
 * Statless Grammar: ValidationContext
 *
 * Revision 1.11  2003/10/01 16:32:41  neilg
 * improve handling of out of memory conditions, bug #23415.  Thanks to David Cargill.
 *
 * Revision 1.10  2003/09/30 21:31:30  peiyongz
 * Implementation of Serialization/Deserialization
 *
 * Revision 1.9  2003/05/16 06:01:57  knoaman
 * Partial implementation of the configurable memory manager.
 *
 * Revision 1.8  2003/05/15 18:53:26  knoaman
 * Partial implementation of the configurable memory manager.
 *
 * Revision 1.7  2002/12/18 14:17:55  gareth
 * Fix to bug #13438. When you eant a vector that calls delete[] on its members you should use RefArrayVectorOf.
 *
 * Revision 1.6  2002/12/16 22:28:25  knoaman
 * Make isAtomic inline.
 *
 * Revision 1.5  2002/12/06 16:45:54  tng
 * header include cleanup.
 *
 * Revision 1.4  2002/11/20 19:57:44  peiyongz
 * validate content as a whole against pattern.
 *
 * Revision 1.3  2002/11/04 14:53:28  tng
 * C++ Namespace Support.
 *
 * Revision 1.2  2002/09/24 19:44:40  tng
 * Performance: use XMLString::equals instead of XMLString::compareString
 *
 * Revision 1.1.1.1  2002/02/01 22:22:42  peiyongz
 * sane_include
 *
 * Revision 1.8  2001/10/11 19:32:27  peiyongz
 * To overwrite inheritFacet()
 *
 * Revision 1.7  2001/10/09 20:56:21  peiyongz
 * inherit from AbstractStringValidator instead of DatatypeValidator to reuse
 * the code.
 *
 * Revision 1.6  2001/10/02 18:59:29  peiyongz
 * Invalid_Facet_Tag to display the tag name
 *
 * Revision 1.5  2001/09/20 13:11:42  knoaman
 * Regx  + misc. fixes
 *
 * Revision 1.4  2001/08/29 19:03:40  peiyongz
 * Bugzilla# 2816:on AIX 4.2, xlC 3 r ev.1, Compilation error on inline method
 *
 * Revision 1.3  2001/08/21 18:42:53  peiyongz
 * Bugzilla# 2816: cleanUp() declared with external linkage and called
 *                          before defined as inline
 *
 * Revision 1.2  2001/08/14 22:11:56  peiyongz
 * new exception message added
 *
 * Revision 1.1  2001/07/11 21:37:17  peiyongz
 * ListDatatypeDTV
 *
 *
 */

// ---------------------------------------------------------------------------
//  Includes
// ---------------------------------------------------------------------------
#include <xercesc/validators/datatype/ListDatatypeValidator.hpp>
#include <xercesc/validators/datatype/InvalidDatatypeFacetException.hpp>
#include <xercesc/validators/datatype/InvalidDatatypeValueException.hpp>
#include <xercesc/util/OutOfMemoryException.hpp>

XERCES_CPP_NAMESPACE_BEGIN

static const int BUF_LEN = 64;
static XMLCh value1[BUF_LEN+1];
static XMLCh value2[BUF_LEN+1];

// ---------------------------------------------------------------------------
//  Constructors and Destructor
// ---------------------------------------------------------------------------
ListDatatypeValidator::ListDatatypeValidator(MemoryManager* const manager)
:AbstractStringValidator(0, 0, 0, DatatypeValidator::List, manager)
,fContent(0)
{}

ListDatatypeValidator::ListDatatypeValidator(
                          DatatypeValidator*            const baseValidator
                        , RefHashTableOf<KVStringPair>* const facets
                        , RefArrayVectorOf<XMLCh>*           const enums
                        , const int                           finalSet
                        , MemoryManager* const manager)
:AbstractStringValidator(baseValidator, facets, finalSet, DatatypeValidator::List, manager)
,fContent(0)
{
    //
    // baseValidator shall either
    // an atomic DTV which servers as itemType, or
    // another ListDTV from which, this ListDTV is derived by restriction.
    //
    // In either case, it shall be not null
    //
    if (!baseValidator)
        ThrowXML(InvalidDatatypeFacetException, XMLExcepts::FACET_List_Null_baseValidator);

    init(enums);
}

ListDatatypeValidator::~ListDatatypeValidator()
{}

DatatypeValidator* ListDatatypeValidator::newInstance
(
      RefHashTableOf<KVStringPair>* const facets
    , RefArrayVectorOf<XMLCh>* const      enums
    , const int                           finalSet
    , MemoryManager* const                manager
)
{
    return (DatatypeValidator*) new (manager) ListDatatypeValidator(this, facets, enums, finalSet, manager);
}


int ListDatatypeValidator::compare(const XMLCh* const lValue
                                 , const XMLCh* const rValue)
{
    DatatypeValidator* theItemTypeDTV = getItemTypeDTV();
    BaseRefVectorOf<XMLCh>* lVector = XMLString::tokenizeString(lValue);
    Janitor<BaseRefVectorOf<XMLCh> > janl(lVector);
    BaseRefVectorOf<XMLCh>* rVector = XMLString::tokenizeString(rValue);
    Janitor<BaseRefVectorOf<XMLCh> > janr(rVector);

    int lNumberOfTokens = lVector->size();
    int rNumberOfTokens = rVector->size();

    if (lNumberOfTokens < rNumberOfTokens)
        return -1;
    else if (lNumberOfTokens > rNumberOfTokens)
        return 1;
    else
    { //compare each token
        for ( int i = 0; i < lNumberOfTokens; i++)
        {
            int returnValue = theItemTypeDTV->compare(lVector->elementAt(i), rVector->elementAt(i));
            if (returnValue != 0)
                return returnValue; //REVISIT: does it make sense to return -1 or +1..?
        }
        return 0;
    }

}

void ListDatatypeValidator::validate( const XMLCh*             const content
                                    ,       ValidationContext* const context)
{
    setContent(content);
    BaseRefVectorOf<XMLCh>* tokenVector = XMLString::tokenizeString(content);
    Janitor<BaseRefVectorOf<XMLCh> > janName(tokenVector);
    checkContent(tokenVector, content, context, false);
}

void ListDatatypeValidator::checkContent( const XMLCh*             const content
                                         ,      ValidationContext* const context
                                         ,      bool                     asBase)
{
    setContent(content);
    BaseRefVectorOf<XMLCh>* tokenVector = XMLString::tokenizeString(content);
    Janitor<BaseRefVectorOf<XMLCh> > janName(tokenVector);
    checkContent(tokenVector, content, context, asBase);
}

//
// here content is a list of items
//
void ListDatatypeValidator::checkContent(       BaseRefVectorOf<XMLCh>*       tokenVector
                                        , const XMLCh*                  const content
                                        ,       ValidationContext*      const context
                                        ,       bool                          asBase)
{
    DatatypeValidator* bv = getBaseValidator();

    if (bv->getType() == DatatypeValidator::List)
        ((ListDatatypeValidator*)bv)->checkContent(tokenVector, content, context, true);
    else
    {   // the ultimate itemType DTV
        for (unsigned int i = 0; i < tokenVector->size(); i++)
            bv->validate(tokenVector->elementAt(i), context);
    }

    int thisFacetsDefined = getFacetsDefined();

    // we check pattern first
    if ( (thisFacetsDefined & DatatypeValidator::FACET_PATTERN ) != 0 )
    {
        // lazy construction
        if (getRegex() == 0)
        {
            try {
                setRegex(new (fMemoryManager) RegularExpression(getPattern(), SchemaSymbols::fgRegEx_XOption, fMemoryManager));
            }
            catch (XMLException &e)
            {
                ThrowXML1(InvalidDatatypeValueException, XMLExcepts::RethrowError, e.getMessage());
            }
        }

        //check every item in the list as a whole
        if (getRegex()->matches(content) == false)
        {
            ThrowXML2(InvalidDatatypeValueException
                    , XMLExcepts::VALUE_NotMatch_Pattern
                    , content
                    , getPattern());
        }

    }

    // if this is a base validator, we only need to check pattern facet
    // all other facet were inherited by the derived type
    if (asBase)
        return;

    unsigned int tokenNumber = tokenVector->size();

    if (((thisFacetsDefined & DatatypeValidator::FACET_MAXLENGTH) != 0) &&
        (tokenNumber > getMaxLength()))
    {
        XMLString::binToText(tokenNumber, value1, BUF_LEN, 10);
        XMLString::binToText(getMaxLength(), value2, BUF_LEN, 10);

        ThrowXML3(InvalidDatatypeValueException
                , XMLExcepts::VALUE_GT_maxLen
                , getContent()
                , value1
                , value2);
    }

    if (((thisFacetsDefined & DatatypeValidator::FACET_MINLENGTH) != 0) &&
        (tokenNumber < getMinLength()))
    {
        XMLString::binToText(tokenNumber, value1, BUF_LEN, 10);
        XMLString::binToText(getMinLength(), value2, BUF_LEN, 10);

        ThrowXML3(InvalidDatatypeValueException
                , XMLExcepts::VALUE_LT_minLen
                , getContent()
                , value1
                , value2);
    }

    if (((thisFacetsDefined & DatatypeValidator::FACET_LENGTH) != 0) &&
        (tokenNumber != AbstractStringValidator::getLength()))
    {
        XMLString::binToText(tokenNumber, value1, BUF_LEN, 10);
        XMLString::binToText(AbstractStringValidator::getLength(), value2, BUF_LEN, 10);

        ThrowXML3(InvalidDatatypeValueException
                , XMLExcepts::VALUE_NE_Len
                , getContent()
                , value1
                , value2);
    }

    if ((thisFacetsDefined & DatatypeValidator::FACET_ENUMERATION) != 0 &&
        (getEnumeration() != 0))
    {
        int i;
        int enumLength = getEnumeration()->size();

        for ( i = 0; i < enumLength; i++)
        {
            //optimization: we do a lexical comparision first
            // this may be faster for string and its derived
            if (XMLString::equals(getEnumeration()->elementAt(i), getContent()))
                break; // a match found

            // do a value space check
            // this is needed for decimal (and probably other types
            // such as datetime related)
            // eg.
            // tokenVector = "1 2 3.0 4" vs enumeration = "1 2 3 4.0"
            //
            if (valueSpaceCheck(tokenVector, getEnumeration()->elementAt(i)))
                break;
        }

        if (i == enumLength)
            ThrowXML1(InvalidDatatypeValueException, XMLExcepts::VALUE_NotIn_Enumeration, getContent());

    } // enumeration

}

bool ListDatatypeValidator::valueSpaceCheck(BaseRefVectorOf<XMLCh>* tokenVector
                                          , const XMLCh* const  enumStr) const
{
    DatatypeValidator* theItemTypeDTV = getItemTypeDTV();
    BaseRefVectorOf<XMLCh>* enumVector = XMLString::tokenizeString(enumStr);
    Janitor<BaseRefVectorOf<XMLCh> > janName(enumVector);

    if (tokenVector->size() != enumVector->size())
        return false;

    for ( unsigned int j = 0; j < tokenVector->size(); j++ )
    {
        if (theItemTypeDTV->compare(tokenVector->elementAt(j), enumVector->elementAt(j)) != 0)
            return false;
    }

    return true;
}

DatatypeValidator* ListDatatypeValidator::getItemTypeDTV() const
{
    DatatypeValidator* bdv = this->getBaseValidator();

    while (bdv->getType() == DatatypeValidator::List)
        bdv = bdv->getBaseValidator();

    return bdv;
}

// ---------------------------------------------------------------------------
//  Utilities
// ---------------------------------------------------------------------------
void ListDatatypeValidator::assignAdditionalFacet( const XMLCh* const key
                                                 , const XMLCh* const)
{
    ThrowXML1(InvalidDatatypeFacetException
            , XMLExcepts::FACET_Invalid_Tag
            , key);
}

void ListDatatypeValidator::inheritAdditionalFacet()
{}

void ListDatatypeValidator::checkAdditionalFacetConstraints() const
{}

void ListDatatypeValidator::checkAdditionalFacet(const XMLCh* const) const
{}

void ListDatatypeValidator::checkValueSpace(const XMLCh* const content)
{}

int ListDatatypeValidator::getLength(const XMLCh* const content) const
{
    BaseRefVectorOf<XMLCh>* tokenVector = XMLString::tokenizeString(content);
    Janitor<BaseRefVectorOf<XMLCh> > janName(tokenVector);

    return tokenVector->size();
}

void ListDatatypeValidator::inspectFacetBase()
{

    //
    // we are pretty sure baseValidator is not null
    //

    if (getBaseValidator()->getType() == DatatypeValidator::List)
    {
        AbstractStringValidator::inspectFacetBase();
    }
    else
    {
        // the first level ListDTV
        // check 4.3.5.c0 must: enumeration values from the value space of base
        if ( ((getFacetsDefined() & DatatypeValidator::FACET_ENUMERATION) != 0) &&
             (getEnumeration() !=0)                                              )
        {
            int i;
            int enumLength = getEnumeration()->size();
            try
            {
                for ( i = 0; i < enumLength; i++)
                {
                    // ask the itemType for a complete check
                    BaseRefVectorOf<XMLCh>* tempList = XMLString::tokenizeString(getEnumeration()->elementAt(i));
                    int tokenNumber = tempList->size();

                    try
                    {
                        for ( int j = 0; j < tokenNumber; j++)
                            getBaseValidator()->validate(tempList->elementAt(j), (ValidationContext*)0);
                    }
                    catch(const OutOfMemoryException&)
                    {
                        throw;
                    }
                    catch (...)
                    {
                        delete tempList;
                        throw;
                    }

                    delete tempList;

                    // enum shall pass this->checkContent() as well.
                    checkContent(getEnumeration()->elementAt(i), (ValidationContext*)0, false);
                }
            }

            catch ( XMLException& )
            {
                ThrowXML1(InvalidDatatypeFacetException
                        , XMLExcepts::FACET_enum_base
                        , getEnumeration()->elementAt(i));
            }

        }

    }

}// End of inspectFacetBase()

void ListDatatypeValidator::inheritFacet()
{

    //iff the base validator is List, then we inherit
    //
    if (getBaseValidator()->getType() == DatatypeValidator::List)
    {
        AbstractStringValidator::inheritFacet();
    }

}

/***
 * 2.5.1.2 List datatypes   
 *   
 * The canonical-lexical-representation for the ·list· datatype is defined as 
 * the lexical form in which each item in the ·list· has the canonical 
 * lexical representation of its ·itemType·.
 ***/
const XMLCh* ListDatatypeValidator::getCanonicalRepresentation(const XMLCh*         const rawData
                                                             ,       MemoryManager* const memMgr) const
{
    ListDatatypeValidator* temp = (ListDatatypeValidator*) this;

    temp->setContent(rawData);
    BaseRefVectorOf<XMLCh>* tokenVector = XMLString::tokenizeString(rawData);
    Janitor<BaseRefVectorOf<XMLCh> > janName(tokenVector);
    temp->checkContent(tokenVector, rawData, 0, false);

    MemoryManager* toUse = memMgr? memMgr : getMemoryManager();
    int  retBufSize = 2 * XMLString::stringLen(rawData);
    XMLCh* retBuf = (XMLCh*) toUse->allocate(retBufSize * sizeof(XMLCh));
    XMLCh* retBufPtr = retBuf;

    DatatypeValidator* itemDv = this->getItemTypeDTV();
    for (unsigned int i = 0; i < tokenVector->size(); i++)
    {
        XMLCh* itemCanRep = (XMLCh*) itemDv->getCanonicalRepresentation(tokenVector->elementAt(i), memMgr);
        XMLString::catString(retBufPtr, itemCanRep);
        retBufPtr = retBufPtr + XMLString::stringLen(itemCanRep) + 1;
        *(retBufPtr++) = chSpace;
        *(retBufPtr) = chNull;
        toUse->deallocate(itemCanRep);
    }

    return 0;
}

/***
 * Support for Serialization/De-serialization
 ***/

IMPL_XSERIALIZABLE_TOCREATE(ListDatatypeValidator)

void ListDatatypeValidator::serialize(XSerializeEngine& serEng)
{
    AbstractStringValidator::serialize(serEng);

    //don't serialize fContent, since it is NOT owned and 
    //will be reset each time validate()/checkContent() invoked.
}

XERCES_CPP_NAMESPACE_END

/**
  * End of file ListDatatypeValidator.cpp
  */
