/*
 * 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: DateTimeValidator.cpp 568078 2007-08-21 11:43:25Z amassari $
 */

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

XERCES_CPP_NAMESPACE_BEGIN

// ---------------------------------------------------------------------------
//  Macro
// ---------------------------------------------------------------------------
#define  REPORT_VALUE_ERROR(val1, val2, except_code, manager)    \
  ThrowXMLwithMemMgr2(InvalidDatatypeValueException               \
          , except_code                                 \
          , val1->getRawData()                          \
          , val2->getRawData()                          \
          , manager);

// ---------------------------------------------------------------------------
//  Constructors and Destructor
// ---------------------------------------------------------------------------
DateTimeValidator::~DateTimeValidator()
{
}

DateTimeValidator::DateTimeValidator(
                          DatatypeValidator*            const baseValidator
                        , RefHashTableOf<KVStringPair>* const facets
                        , const int                           finalSet
                        , const ValidatorType                 type
                        , MemoryManager* const                manager)
:AbstractNumericFacetValidator(baseValidator, facets, finalSet, type, manager)
{
    //do not invoke init() here !!!
}

void DateTimeValidator::validate(const XMLCh*             const content
                               ,       ValidationContext* const context
                               ,       MemoryManager*     const manager)
{
    checkContent(content, context, false, manager);
}

int DateTimeValidator::compare(const XMLCh* const value1
                             , const XMLCh* const value2
                             , MemoryManager* const manager)
{
    try
    {
        XMLDateTime *pDate1 = parse(value1, manager);
        Janitor<XMLDateTime> jName1(pDate1);
        XMLDateTime *pDate2 = parse(value2, manager);
        Janitor<XMLDateTime> jName2(pDate2);
        int result = compareDates(pDate1, pDate2, true);
        return (result==INDETERMINATE)? -1 : result;
    }
    catch(const OutOfMemoryException&)
    {
        throw;
    }
    catch (...) // RuntimeException e
    {
        return -1; // revisit after implement compareDates()
    }

}


void DateTimeValidator::checkContent(const XMLCh*             const content
                                   ,       ValidationContext* const context
                                   ,       bool                     asBase
                                   ,       MemoryManager*     const manager)
{
    //validate against base validator if any
    DateTimeValidator *pBaseValidator = (DateTimeValidator*) this->getBaseValidator();
    if (pBaseValidator)
        pBaseValidator->checkContent(content, context, true, manager);

    int thisFacetsDefined = getFacetsDefined();

    // we check pattern first
    if ( (thisFacetsDefined & DatatypeValidator::FACET_PATTERN ) != 0 )
    {
        if (getRegex()->matches(content, manager) ==false)
        {
            ThrowXMLwithMemMgr2(InvalidDatatypeValueException
                    , XMLExcepts::VALUE_NotMatch_Pattern
                    , content
                    , getPattern()
                    , manager);
        }
    }

    // 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;

    // the derived classes' parse() method constructs an
    // XMLDateTime object anc invokes appropriate XMLDateTime's
    // parser to parse the content.
    XMLDateTime dateTimeValue(content, manager);
    XMLDateTime* dateTime = &dateTimeValue;
    
    parse(dateTime);

    // must be < MaxExclusive
    if ((thisFacetsDefined & DatatypeValidator::FACET_MAXEXCLUSIVE) != 0)
    {
        if (compareValues(dateTime, getMaxExclusive()) != XMLDateTime::LESS_THAN)
        {
            REPORT_VALUE_ERROR( dateTime
                              , getMaxExclusive()
                              , XMLExcepts::VALUE_exceed_maxExcl
                              , manager)
        }
    } 	

    // must be <= MaxInclusive
    if ((thisFacetsDefined & DatatypeValidator::FACET_MAXINCLUSIVE) != 0)
    {
        int result = compareValues(dateTime, getMaxInclusive());
        if ( result == XMLDateTime::GREATER_THAN || result == XMLDateTime::INDETERMINATE )
        {
            REPORT_VALUE_ERROR( dateTime
                              , getMaxInclusive()
                              , XMLExcepts::VALUE_exceed_maxIncl
                              , manager)
        }
    }

    // must be >= MinInclusive
    if ((thisFacetsDefined & DatatypeValidator::FACET_MININCLUSIVE) != 0)
    {
        int result = compareValues(dateTime, getMinInclusive());
        if (result == XMLDateTime::LESS_THAN || result == XMLDateTime::INDETERMINATE)
        {
            REPORT_VALUE_ERROR( dateTime
                              , getMinInclusive()
                              , XMLExcepts::VALUE_exceed_minIncl
                              , manager)
        }
    }

    // must be > MinExclusive
    if ( (thisFacetsDefined & DatatypeValidator::FACET_MINEXCLUSIVE) != 0 )
    {
        if (compareValues(dateTime, getMinExclusive()) != XMLDateTime::GREATER_THAN)
        {
            REPORT_VALUE_ERROR( dateTime
                              , getMinExclusive()
                              , XMLExcepts::VALUE_exceed_minExcl
                              , manager)
        }
    }

    if ((thisFacetsDefined & DatatypeValidator::FACET_ENUMERATION) != 0 &&
        (getEnumeration() != 0))
    {
        int i=0;
        int enumLength = getEnumeration()->size();
        for ( ; i < enumLength; i++)
        {
            if (compareValues(dateTime, getEnumeration()->elementAt(i)) == XMLDateTime::EQUAL)
                break;
        }

        if (i == enumLength)
            ThrowXMLwithMemMgr1(InvalidDatatypeValueException, XMLExcepts::VALUE_NotIn_Enumeration, content, manager);
    }
}

//
// Comparision methods
//
int DateTimeValidator::compareValues(const XMLNumber* const lValue
                                   , const XMLNumber* const rValue)
{
    return compareDates((XMLDateTime*) lValue, (XMLDateTime*) rValue, true);
}

/**
 * Compare algorithm described in dateDime (3.2.7).
 * Duration datatype overwrites this method
 *
 * @param date1  normalized date representation of the first value
 * @param date2  normalized date representation of the second value
 * @param strict
 * @return less, greater, less_equal, greater_equal, equal
 */
int DateTimeValidator::compareDates(const XMLDateTime* const date1
                                  , const XMLDateTime* const date2
                                  , bool)
{
    return XMLDateTime::compare(date1, date2);
}

//
// In fact, the proper way of the following set*() shall be
// {
// if (fMaxInclusive)
//     delete fMaxInclusive;
//
//    fMaxInclusive = parse(value);
//
// }
//
// But we know this function is invoked once and only once
// since there is no duplicated facet passed in, therefore
// fMaxInclusive is alwasy zero before, so for the
// sake of performance, we do not do the checking/delete.
//

void DateTimeValidator::setMaxInclusive(const XMLCh* const value)
{
    fMaxInclusive = parse(value, fMemoryManager);
}

void DateTimeValidator::setMaxExclusive(const XMLCh* const value)
{
    fMaxExclusive = parse(value, fMemoryManager);
}

void DateTimeValidator::setMinInclusive(const XMLCh* const value)
{
    fMinInclusive = parse(value, fMemoryManager);
}

void DateTimeValidator::setMinExclusive(const XMLCh* const value)
{
    fMinExclusive = parse(value, fMemoryManager);
}

void DateTimeValidator::setEnumeration(MemoryManager* const)
{
// to do: do we need to check against base value space???

    if (!fStrEnumeration)
        return;

    int enumLength = fStrEnumeration->size();
    fEnumeration = new (fMemoryManager) RefVectorOf<XMLNumber>(enumLength, true, fMemoryManager);
    fEnumerationInherited = false;

    for ( int i = 0; i < enumLength; i++)
        fEnumeration->insertElementAt(parse(fStrEnumeration->elementAt(i), fMemoryManager), i);

}

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

IMPL_XSERIALIZABLE_NOCREATE(DateTimeValidator)

void DateTimeValidator::serialize(XSerializeEngine& serEng)
{
    /***
     *
     * Note: All its derivatives share the same number type, that is
     *       XMLNumber::DateTime, so this class would write it.
     ***/

    if (serEng.isStoring())
    {
        serEng<<(int) XMLNumber::DateTime;
    }

    AbstractNumericFacetValidator::serialize(serEng);

    //dateTime can be instantiated during checkContent(), so don't serialize it.
}

XERCES_CPP_NAMESPACE_END

/**
  * End of file DateTimeValidator::cpp
  */

