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


// ---------------------------------------------------------------------------
//  Includes
// ---------------------------------------------------------------------------
#include <xercesc/util/XMLUniDefs.hpp>
#include <xercesc/util/XMLUni.hpp>
#include <xercesc/util/XMLRegisterCleanup.hpp>
#include <xercesc/util/XMLInitializer.hpp>
#include <xercesc/validators/DTD/DTDGrammar.hpp>
#include <xercesc/validators/DTD/XMLDTDDescriptionImpl.hpp>

#include <xercesc/internal/XTemplateSerializer.hpp>

XERCES_CPP_NAMESPACE_BEGIN

// ---------------------------------------------------------------------------
//  Local static data
// ---------------------------------------------------------------------------
static bool               sEntityPoolMutexRegistered = false;
static XMLMutex*          sEntityPoolMutex = 0;
static XMLRegisterCleanup entityPoolRegistryCleanup;

// ---------------------------------------------------------------------------
//  DTDGrammar: Static member data
// ---------------------------------------------------------------------------
NameIdPool<DTDEntityDecl>* DTDGrammar::fDefaultEntities = 0;

void XMLInitializer::initializeDTDGrammarDfltEntities()
{
    DTDGrammar::fDefaultEntities = new NameIdPool<DTDEntityDecl>(11, 12);

    // Add the default entity entries for the character refs that must
    // always be present. We indicate that they are from the internal
    // subset. They aren't really, but they have to look that way so
    // that they are still valid for use within a standalone document.
    //
    // We also mark them as special char entities, which allows them
    // to be used in places whether other non-numeric general entities
    // cannot.
    //
    if (DTDGrammar::fDefaultEntities)
    {
        DTDGrammar::fDefaultEntities->put(new DTDEntityDecl(XMLUni::fgAmp, chAmpersand, true, true));
        DTDGrammar::fDefaultEntities->put(new DTDEntityDecl(XMLUni::fgLT, chOpenAngle, true, true));
        DTDGrammar::fDefaultEntities->put(new DTDEntityDecl(XMLUni::fgGT, chCloseAngle, true, true));
        DTDGrammar::fDefaultEntities->put(new DTDEntityDecl(XMLUni::fgQuot, chDoubleQuote, true, true));
        DTDGrammar::fDefaultEntities->put(new DTDEntityDecl(XMLUni::fgApos, chSingleQuote, true, true));

        // register cleanup method
        entityPoolRegistryCleanup.registerCleanup(DTDGrammar::reinitDfltEntities);
        sEntityPoolMutexRegistered = true;
    }
}

//---------------------------------------------------------------------------
//  DTDGrammar: Constructors and Destructor
// ---------------------------------------------------------------------------
DTDGrammar::DTDGrammar(MemoryManager* const manager) :
    fMemoryManager(manager)
    , fElemDeclPool(0)
    , fElemNonDeclPool(0)
    , fEntityDeclPool(0)
    , fNotationDeclPool(0)
    , fGramDesc(0)
    , fRootElemId(0)
    , fValidated(false)
{
    //
    //  Init all the pool members.
    //
    //  <TBD> Investigate what the optimum values would be for the various
    //  pools.
    //
    fElemDeclPool = new (fMemoryManager) NameIdPool<DTDElementDecl>(109, 128, fMemoryManager);
    // should not need this in the common situation where grammars
    // are built once and then read - NG
    //fElemNonDeclPool = new (fMemoryManager) NameIdPool<DTDElementDecl>(29, 128, fMemoryManager);
    fEntityDeclPool = new (fMemoryManager) NameIdPool<DTDEntityDecl>(109, 128, fMemoryManager);
    fNotationDeclPool = new (fMemoryManager) NameIdPool<XMLNotationDecl>(109, 128, fMemoryManager);

    //REVISIT: use grammarPool to create
    fGramDesc = new (fMemoryManager) XMLDTDDescriptionImpl(XMLUni::fgDTDEntityString, fMemoryManager);

    // Create default entities
    resetEntityDeclPool();
}

DTDGrammar::~DTDGrammar()
{
    delete fElemDeclPool;
    if(fElemNonDeclPool) 
    {
        delete fElemNonDeclPool;
    }
    delete fEntityDeclPool;
    delete fNotationDeclPool;
    delete fGramDesc;
}

// -----------------------------------------------------------------------
//  Notification that lazy data has been deleted
// -----------------------------------------------------------------------
void DTDGrammar::reinitDfltEntities() {

    delete fDefaultEntities;
    fDefaultEntities = 0;

    // delete local static data
    delete sEntityPoolMutex;
    sEntityPoolMutex = 0;
    sEntityPoolMutexRegistered = false;
}

// -----------------------------------------------------------------------
//  Virtual methods
// -----------------------------------------------------------------------
XMLElementDecl* DTDGrammar::findOrAddElemDecl (const   unsigned int    uriId
        , const XMLCh* const    baseName
        , const XMLCh* const
        , const XMLCh* const    qName
        , unsigned int          scope
        ,       bool&           wasAdded )
{
    // See it it exists
    DTDElementDecl* retVal = (DTDElementDecl*) getElemDecl(uriId, baseName, qName, scope);

    // if not, then add this in
    if (!retVal)
    {
        retVal = new (fMemoryManager) DTDElementDecl
        (
            qName
            , uriId
            , DTDElementDecl::Any
            , fMemoryManager
        );
        if(!fElemNonDeclPool)
            fElemNonDeclPool = new (fMemoryManager) NameIdPool<DTDElementDecl>(29, 128, fMemoryManager);
        const unsigned int elemId = fElemNonDeclPool->put(retVal);
        retVal->setId(elemId);
        wasAdded = true;
    }
     else
    {
        wasAdded = false;
    }
    return retVal;
}

XMLElementDecl* DTDGrammar::putElemDecl (const   unsigned int    uriId
        , const XMLCh* const
        , const XMLCh* const
        , const XMLCh* const    qName
        , unsigned int
        , const bool            notDeclared)
{
    DTDElementDecl* retVal = new (fMemoryManager) DTDElementDecl
    (
        qName
        , uriId
        , DTDElementDecl::Any
        , fMemoryManager
    );
    if(notDeclared)
    {
        if(!fElemNonDeclPool)
            fElemNonDeclPool = new (fMemoryManager) NameIdPool<DTDElementDecl>(29, 128, fMemoryManager);
        retVal->setId(fElemNonDeclPool->put(retVal));
    } else 
    {
        retVal->setId(fElemDeclPool->put(retVal));
    }
    return retVal;
}

void DTDGrammar::reset()
{
    //
    //  We need to reset all of the pools.
    //
    fElemDeclPool->removeAll();
    // now that we have this, no point in deleting it...
    if(fElemNonDeclPool)
        fElemNonDeclPool->removeAll();
    fNotationDeclPool->removeAll();
    fEntityDeclPool->removeAll();
    fValidated = false;
}

void DTDGrammar::resetEntityDeclPool() {

    // Initialize default entities if not initialized
    if (!sEntityPoolMutexRegistered)
    {
        if (!sEntityPoolMutex)
        {
            XMLMutexLock lock(XMLPlatformUtils::fgAtomicMutex);
            if (!sEntityPoolMutex)
                sEntityPoolMutex = new XMLMutex(XMLPlatformUtils::fgMemoryManager);
        }

        // Use a faux scope to synchronize while we do this
        {
            XMLMutexLock lock(sEntityPoolMutex);

            // If we got here first, then register it and set the registered flag
            if (!sEntityPoolMutexRegistered)
            {
                fDefaultEntities = new NameIdPool<DTDEntityDecl>(11, 12);

                //
                // Add the default entity entries for the character refs that must
                // always be present. We indicate that they are from the internal
                // subset. They aren't really, but they have to look that way so
                // that they are still valid for use within a standalone document.
                //
                // We also mark them as special char entities, which allows them
                // to be used in places whether other non-numeric general entities
                // cannot.
                //
                fDefaultEntities->put(new DTDEntityDecl(XMLUni::fgAmp, chAmpersand, true, true));
                fDefaultEntities->put(new DTDEntityDecl(XMLUni::fgLT, chOpenAngle, true, true));
                fDefaultEntities->put(new DTDEntityDecl(XMLUni::fgGT, chCloseAngle, true, true));
                fDefaultEntities->put(new DTDEntityDecl(XMLUni::fgQuot, chDoubleQuote, true, true));
                fDefaultEntities->put(new DTDEntityDecl(XMLUni::fgApos, chSingleQuote, true, true));

                // register cleanup method
                entityPoolRegistryCleanup.registerCleanup(DTDGrammar::reinitDfltEntities);
                sEntityPoolMutexRegistered = true;
            }
        }
    }
}

void DTDGrammar::setGrammarDescription( XMLGrammarDescription* gramDesc)
{
    if ((!gramDesc) || 
        (gramDesc->getGrammarType() != Grammar::DTDGrammarType))
        return;

    if (fGramDesc)
        delete fGramDesc;

    //adopt the grammar Description
    fGramDesc = (XMLDTDDescription*) gramDesc;
}

XMLGrammarDescription*  DTDGrammar::getGrammarDescription() const
{
    return fGramDesc;
}

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

IMPL_XSERIALIZABLE_TOCREATE(DTDGrammar)

void DTDGrammar::serialize(XSerializeEngine& serEng)
{

    Grammar::serialize(serEng);

    //don't serialize fDefaultEntities

    if (serEng.isStoring())
    {
        /***
         *
         * Serialize NameIdPool<DTDElementDecl>*       fElemDeclPool;
         * Serialize NameIdPool<DTDEntityDecl>*        fEntityDeclPool;
         * Serialize NameIdPool<XMLNotationDecl>*      fNotationDeclPool;
         ***/
        XTemplateSerializer::storeObject(fElemDeclPool, serEng);
        XTemplateSerializer::storeObject(fEntityDeclPool, serEng);
        XTemplateSerializer::storeObject(fNotationDeclPool, serEng);

        /***
         * serialize() method shall be used to store object
         * which has been created in ctor
         ***/
        fGramDesc->serialize(serEng);

        serEng<<fRootElemId;
        serEng<<fValidated;
    }
    else
    {

       /***
         *
         * Deserialize NameIdPool<DTDElementDecl>*       fElemDeclPool;
         * Deserialize NameIdPool<DTDEntityDecl>*        fEntityDeclPool;
         * Deerialize NameIdPool<XMLNotationDecl>*       fNotationDeclPool;
         ***/
        XTemplateSerializer::loadObject(&fElemDeclPool, 109, 128, serEng);
        fElemNonDeclPool = 0;
        XTemplateSerializer::loadObject(&fEntityDeclPool, 109, 128, serEng);
        XTemplateSerializer::loadObject(&fNotationDeclPool, 109, 128, serEng);

        /***
         * serialize() method shall be used to load object
         * which has been created in ctor
         ***/
        fGramDesc->serialize(serEng);

        serEng>>fRootElemId;
        serEng>>fValidated;
    }

}

XERCES_CPP_NAMESPACE_END
