// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.

// $Id$

#ifndef __RRPARAMREGISTRY_H
#define __RRPARAMREGISTRY_H 1

#include <string>

#include <stdint.h>

#include <boost/shared_ptr.hpp>

#include <exceptions/exceptions.h>

#include <dns/rdata.h>

namespace isc {
namespace dns {

// forward declarations
struct RRParamRegistryImpl;

///
/// \brief A standard DNS module exception that is thrown if a new RR type is
/// being registered with a different type string.
///
class RRTypeExists : public Exception {
public:
    RRTypeExists(const char* file, size_t line, const char* what) :
        isc::Exception(file, line, what) {}
};

///
/// \brief A standard DNS module exception that is thrown if a new RR class is
/// being registered with a different type string.
///
class RRClassExists : public Exception {
public:
    RRClassExists(const char* file, size_t line, const char* what) :
        isc::Exception(file, line, what) {}
};

namespace rdata {
/// \brief The \c AbstractRdataFactory class is an abstract base class to
/// encapsulate a set of Rdata factory methods in a polymorphic way.
///
/// An external developers who want to introduce a new or experimental RR type
/// are expected to define a corresponding derived class of \c
/// AbstractRdataFactory and register it via \c RRParamRegistry.
///
/// For other users of this API normally do not have to care about this class
/// or its derived classes; this class is generally intended to be used
/// as an internal utility of the API implementation.
class AbstractRdataFactory {
    ///
    /// \name Constructors and Destructor
    ///
    //@{
protected:
    /// The default constructor
    ///
    /// This is intentionally defined as \c protected as this base class should
    /// never be instantiated (except as part of a derived class).
    AbstractRdataFactory() {}
public:
    /// The destructor.
    virtual ~AbstractRdataFactory() {};
    //@}

    ///
    /// \name Factory methods for polymorphic creation.
    ///
    //@{
    ///
    /// \brief Create RDATA from a string.
    ///
    /// This method creates from a string an \c Rdata object of specific class
    /// corresponding to the specific derived class of \c AbstractRdataFactory.
    ///
    /// \param rdata_str A string of textual representation of the \c Rdata.
    /// \return An \c RdataPtr object pointing to the created \c Rdata object.
    virtual RdataPtr create(const std::string& rdata_str) const = 0;
    ///
    /// \brief Create RDATA from wire-format data.
    ///
    /// This method creates from wire-format binary data an \c Rdata object
    /// of specific class corresponding to the specific derived class of
    /// \c AbstractRdataFactory.
    ///
    /// \param buffer A reference to an \c InputBuffer object storing the
    /// \c Rdata to parse.
    /// \param rdata_len The length in buffer of the \c Rdata.  In bytes.
    /// \return An \c RdataPtr object pointing to the created \c Rdata object.
    virtual RdataPtr create(InputBuffer& buffer, size_t rdata_len) const = 0;
    ///
    /// \brief Create RDATA from another \c Rdata object of the same type.
    ///
    /// This method creates an \c Rdata object of specific class corresponding
    /// to the specific derived class of \c AbstractRdataFactory, copying the
    /// content of the given \c Rdata, \c source.
    ///
    /// \c source must be an object of the concrete derived class corresponding
    /// to the specific derived class of \c AbstractRdataFactory;
    /// otherwise, an exception of class \c std::bad_cast will be thrown.
    ///
    /// \param source A reference to an \c Rdata object whose content is to
    /// be copied to the created \c Rdata object.
    /// \return An \c RdataPtr object pointing to the created \c Rdata object.
    virtual RdataPtr create(const rdata::Rdata& source) const = 0;
    //@}
};

///
/// The \c RdataFactoryPtr type is a pointer-like type, pointing to an
/// object of some concrete derived class of \c AbstractRdataFactory.
///
typedef boost::shared_ptr<AbstractRdataFactory> RdataFactoryPtr;
} // end of namespace rdata

///
/// The \c RRParamRegistry class represents a registry of parameters to
/// manipulate DNS resource records (RRs).
///
/// A \c RRParamRegistry class object stores a mapping between RR types or
/// classes and their textual representations.  It will also have knowledge of
/// how to create an RDATA object for a specific pair of RR type and class
/// (not implemented in this version).
///
/// Normal applications that only handle standard DNS protocols won't have to
/// care about this class.  This is mostly an internal class to the DNS library
/// to manage standard parameters.  Some advanced applications may still need
/// to use this class explicitly.  For example, if an application wants to
/// define and use an experimental non-standard RR type, it may want to register
/// related protocol parameters for its convenience.  This class is designed to
/// allow such usage without modifying the library source code or rebuilding
/// the library.
///
/// It is assumed that at most one instance of this class can exist so that
/// the application uses the consistent set of registered parameters.  To ensure
/// this, this class is designed and implemented as a "singleton class": the
/// constructor is intentionally private, and applications must get access to
/// the single instance via the \c getRegistry() static member function.
///
/// In the current implementation, access to the singleton \c RRParamRegistry
/// object is not thread safe.
/// The application should ensure that multiple threads don't race in the
/// first invocation of \c getRegistry(), and, if the registry needs to
/// be changed dynamically, read and write operations are performed
/// exclusively.
/// Since this class should be static in common usage this restriction would
/// be acceptable in practice.
/// In the future, we may extend the implementation so that multiple threads can
/// get access to the registry fully concurrently without any restriction.
///
/// Note: the implementation of this class is incomplete: we should at least
/// add RDATA related parameters.  This will be done in a near future version,
/// at which point some of method signatures will be changed.
class RRParamRegistry {
    ///
    /// \name Constructors and Destructor
    ///
    /// These are intentionally hidden (see the class description).
    //@{
private:
    RRParamRegistry();
    RRParamRegistry(const RRParamRegistry& orig);
    ~RRParamRegistry();
    //@}
public:
    ///
    /// \brief Return the singleton instance of \c RRParamRegistry.
    ///
    /// This method is a unified access point to the singleton instance of
    /// the RR parameter registry (\c RRParamRegistry).
    /// On first invocation it internally constructs an instance of the
    /// \c RRParamRegistry class and returns a reference to it.
    /// This is a static object inside this method and will remain valid
    /// throughout the rest of the application lifetime.
    /// On subsequent calls this method simply returns a reference to the
    /// singleton object.
    ///
    /// If resource allocation fails in the first invocation,
    /// a corresponding standard exception will be thrown.
    /// This method never fails otherwise.  In particular, this method
    /// doesn't throw an exception once the singleton instance is constructed.
    ///
    /// \return A reference to the singleton instance of \c RRParamRegistry.
    static RRParamRegistry& getRegistry();

    ///
    /// \name Registry Update Methods
    ///
    //@{
    ///
    /// \brief Add a set of parameters for a pair of RR type and class.
    ///
    /// This method adds to the registry a specified set of RR parameters,
    /// including mappings between type/class codes and their textual
    /// representations.
    ///
    /// Regarding the mappings between textual representations and integer
    /// codes, this method behaves in the same way as \c addType() and
    /// \c addClass().  That is, it ignores any overriding attempt as
    /// long as the mapping is the same; otherwise the corresponding exception
    /// will be thrown.
    ///
    /// This method provides the strong exception guarantee: unless an
    /// exception is thrown the entire specified set of parameters must be
    /// stored in the registry; if this method throws an exception the
    /// registry will remain in the state before this method is called.
    ///
    /// \param type_string The textual representation of the RR type.
    /// \param type_code The integer code of the RR type.
    /// \param class_string The textual representation of the RR class.
    /// \param class_code The integer code of the RR class.
    /// \param rdata_factory An \c RdataFactoryPtr object pointing to a
    /// concrete RDATA factory.
    void add(const std::string& type_string, uint16_t type_code,
             const std::string& class_string, uint16_t class_code,
             rdata::RdataFactoryPtr rdata_factory);

    /// \brief Add a set of parameters for a class-independent RR type.
    ///
    /// This method behaves as exactly same as the other \c add method except
    /// that it handles class-independent types (such as NS, CNAME, or SOA).
    ///
    /// \param type_string The textual representation of the RR type.
    /// \param type_code The integer code of the RR type.
    /// \param rdata_factory An \c RdataFactoryPtr object pointing to a
    /// concrete RDATA factory.
    void add(const std::string& type_string, uint16_t type_code,
             rdata::RdataFactoryPtr rdata_factory);

    /// \brief Add mappings between RR type code and textual representation.
    ///
    /// This method adds a mapping from the type code of an RR to its textual
    /// representation and the reverse mapping in the registry.
    ///
    /// If the given RR type is already registered with the same textual
    /// representation, this method simply ignores the duplicate mapping;
    /// if the given type is registered and a new pair with a different
    /// textual representation is being added,an exception of class
    /// \c RRTypeExist will be thrown.
    /// To replace an existing mapping with a different textual representation,
    /// the existing one must be removed by the \c removeType() method
    /// beforehand.
    ///
    /// In addition, if resource allocation for the new mapping entries fails,
    /// a corresponding standard exception will be thrown.
    ///
    /// This method provides the strong exception guarantee: unless an exception
    /// is thrown the specified mappings must be stored in the registry
    /// (although it may be an already existing one) on completion of the
    /// method; if this method throws an exception the registry will remain
    /// in the state before this method is called.
    ///
    /// \param type_string The textual representation of the RR type.
    /// \param type_code The integer code of the RR type.
    /// \return \c true if a new mapping is added to the repository; \c false
    /// if the same mapping is already registered.
    bool addType(const std::string& type_string, uint16_t type_code);

    /// \brief Remove mappings between RR type code and textual representation
    /// for a given type.
    ///
    /// This method can safely be called whether or not the specified mappings
    /// exist in the registry.  If not, this method simply ignores the attempt
    /// and returns \c false.
    ///
    /// This method never throws an exception.
    ///
    /// \param type_code The integer code of the RR type.
    /// \return \c true if mappings for the specified RR type exists and is
    /// removed; \c false if no such mapping is in the registry.
    bool removeType(uint16_t type_code);

    /// \brief Add mappings between RR class code and textual representation.
    ///
    /// This method adds a mapping from the class code of an RR to its textual
    /// representation and the reverse mapping in the registry.
    ///
    /// If the given RR class is already registered with the same textual
    /// representation, this method simply ignores the duplicate mapping;
    /// if the given class is registered and a new pair with a different
    /// textual representation is being added,an exception of class
    /// \c RRClassExist will be thrown.
    /// To replace an existing mapping with a different textual representation,
    /// the existing one must be removed by the \c removeClass() method
    /// beforehand.
    ///
    /// In addition, if resource allocation for the new mapping entries fails,
    /// a corresponding standard exception will be thrown.
    ///
    /// This method provides the strong exception guarantee: unless an exception
    /// is thrown the specified mappings must be stored in the registry
    /// (although it may be an already existing one) on completion of the
    /// method; if this method throws an exception the registry will remain
    /// in the state before this method is called.
    ///
    /// \param class_string The textual representation of the RR class.
    /// \param class_code The integer code of the RR class.
    /// \return \c true if a new mapping is added to the repository; \c false
    /// if the same mapping is already registered.
    bool addClass(const std::string& class_string, uint16_t class_code);

    /// \brief Remove mappings between RR class code and textual representation
    /// for a given class.
    ///
    /// This method can safely be called whether or not the specified mappings
    /// exist in the registry.  If not, this method simply ignores the attempt
    /// and returns \c false.
    ///
    /// This method never throws an exception.
    ///
    /// \param class_code The integer code of the RR class.
    /// \return \c true if mappings for the specified RR type exists and is
    /// removed; \c false if no such mapping is in the registry.
    bool removeClass(uint16_t class_code);

    /// \brief Remove registered RDATA factory for the given pair of \c RRType
    /// and \c RRClass.
    ///
    /// This method can safely be called whether or not the specified factory
    /// object exist in the registry.  If not, this method simply ignores the
    /// attempt and returns \c false.
    ///
    /// This method never throws an exception.
    ///
    /// \param rrtype An \c RRType object specifying the type/class pair.
    /// \param rrclass An \c RRClass object specifying the type/class pair.
    /// \return \c true if a factory object for the specified RR type/class
    /// pair exists and is removed; \c false if no such object is in the
    /// registry.
    bool removeRdataFactory(const RRType& rrtype, const RRClass& rrclass);

    /// \brief Remove registered RDATA factory for the given pair of \c RRType
    /// and \c RRClass.
    ///
    /// This method can safely be called whether or not the specified factory
    /// object exist in the registry.  If not, this method simply ignores the
    /// attempt and returns \c false.
    ///
    /// This method never throws an exception.
    ///
    /// \param rrtype An \c RRType object specifying the type/class pair.
    /// \return \c true if a factory object for the specified RR type/class
    /// pair exists and is removed; \c false if no such object is in the
    /// registry.
    bool removeRdataFactory(const RRType& rrtype);
    //@}

    ///
    /// \name Parameter Conversion Methods
    ///
    //@{
    /// \brief Convert a textual representation of an RR type to the
    /// corresponding 16-bit integer code.
    ///
    /// This method searches the \c RRParamRegistry for the mapping from the
    /// given textual representation of RR type to the corresponding integer
    /// code.  If a mapping is found, it returns the associated type code;
    /// otherwise, if the given string is in the form of "TYPEnnnn", it returns
    /// the corresponding number as the type code; otherwise, it throws an
    /// exception of class \c InvalidRRType.
    ///
    /// \param type_string The textual representation of the RR type.
    /// \return The RR type code for \c type_string.
    uint16_t textToTypeCode(const std::string& type_string) const;

    /// \brief Convert type code into its textual representation.
    ///
    /// This method searches the \c RRParamRegistry for the mapping from the
    /// given RR type code to its textual representation.
    /// If a mapping is found, it returns (a copy of) the associated string;
    /// otherwise, this method creates a new string in the form of "TYPEnnnn"
    /// where "nnnn" is the decimal representation of the type code, and
    /// returns the new string.
    ///
    /// If resource allocation for the returned string fails,
    /// a corresponding standard exception will be thrown.
    /// This method never fails otherwise.
    ///
    /// \param type_code The integer code of the RR type.
    /// \return A textual representation of the RR type for code \c type_code.
    std::string codeToTypeText(uint16_t type_code) const;

    /// \brief Convert a textual representation of an RR class to the
    /// corresponding 16-bit integer code.
    ///
    /// This method searches the \c RRParamRegistry for the mapping from the
    /// given textual representation of RR class to the corresponding integer
    /// code.  If a mapping is found, it returns the associated class code;
    /// otherwise, if the given string is in the form of "CLASSnnnn", it returns
    /// the corresponding number as the class code; otherwise, it throws an
    /// exception of class \c InvalidRRClass.
    ///
    /// \param class_string The textual representation of the RR class.
    /// \return The RR class code for \c class_string.
    uint16_t textToClassCode(const std::string& class_string) const;

    /// \brief Convert class code into its textual representation.
    ///
    /// This method searches the \c RRParamRegistry for the mapping from the
    /// given RR class code to its textual representation.
    /// If a mapping is found, it returns (a copy of) the associated string;
    /// otherwise, this method creates a new string in the form of "CLASSnnnn"
    /// where "nnnn" is the decimal representation of the class code, and
    /// returns the new string.
    ///
    /// If resource allocation for the returned string fails,
    /// a corresponding standard exception will be thrown.
    /// This method never fails otherwise.
    ///
    /// \param class_code The integer code of the RR class.
    /// \return A textual representation of the RR class for code \c class_code.
    std::string codeToClassText(uint16_t class_code) const;
    //@}

    ///
    /// \name RDATA Factories
    ///
    /// This set of methods provide a unified interface to create an
    /// \c rdata::Rdata object in a parameterized polymorphic way,
    /// that is, these methods take a pair of \c RRType and \c RRClass
    /// objects and data specific to that pair, and create an object of
    /// the corresponding concrete derived class of \c rdata::Rdata.
    ///
    /// These methods first search the \c RRParamRegistry for a factory
    /// method (a member of a concrete derived class of
    /// \c AbstractRdataFactory) for the given RR type and class pair.
    /// If the search fails, they then search for a factory method for
    /// the given type ignoring the class, in case a RRClass independent
    /// factory method is registered.
    /// If it still fails, these methods assume the RDATA is of an "unknown"
    /// type, and creates a new object by calling a constructor of the
    /// \c rdata::generic::Generic class.
    ///
    //@{
    /// \brief Create RDATA of a given pair of RR type and class from a string.
    ///
    /// This method creates from a string an \c Rdata object of the given pair
    /// of RR type and class.
    ///
    /// \param rrtype An \c RRType object specifying the type/class pair.
    /// \param rrclass An \c RRClass object specifying the type/class pair.
    /// \param rdata_string A string of textual representation of the \c Rdata.
    /// \return An \c rdata::RdataPtr object pointing to the created \c Rdata
    /// object.
    rdata::RdataPtr createRdata(const RRType& rrtype, const RRClass& rrclass,
                                const std::string& rdata_string);
    /// \brief Create RDATA of a given pair of RR type and class from
    /// wire-format data.
    ///
    /// This method creates from wire-format binary data an \c Rdata object
    /// of the given pair of RR type and class.
    ///
    /// \param rrtype An \c RRType object specifying the type/class pair.
    /// \param rrclass An \c RRClass object specifying the type/class pair.
    /// \param buffer A reference to an \c InputBuffer object storing the
    /// \c Rdata to parse.
    /// \param len The length in buffer of the \c Rdata.  In bytes.
    /// \return An \c rdata::RdataPtr object pointing to the created \c Rdata
    /// object.
    rdata::RdataPtr createRdata(const RRType& rrtype, const RRClass& rrclass,
                                InputBuffer& buffer, size_t len);
    /// \brief Create RDATA of a given pair of RR type and class, copying
    /// of another RDATA of same kind.
    ///
    /// This method creates an \c Rdata object of the given pair of
    /// RR type and class, copying the  content of the given \c Rdata,
    /// \c source.
    ///
    /// \c source must be an object of the concrete derived class of
    /// \c rdata::Rdata for the given pair of RR type and class;
    /// otherwise, an exception of class \c std::bad_cast will be thrown.
    /// In case the \c RRParamRegistry doesn't have a factory method for
    /// the given pair and it is assumed to be of an "unknown" type,
    /// \c source must reference an object of class
    /// \c rdata::generic::Generic; otherwise, an exception of class
    /// \c std::bad_cast will be thrown.
    ///
    /// \param rrtype An \c RRType object specifying the type/class pair.
    /// \param rrclass An \c RRClass object specifying the type/class pair.
    /// \param source A reference to an \c rdata::Rdata object whose content
    /// is to be copied to the created \c rdata::Rdata object.
    /// \return An \c rdata::RdataPtr object pointing to the created
    /// \c rdata::Rdata object.
    rdata::RdataPtr createRdata(const RRType& rrtype, const RRClass& rrclass,
                                const rdata::Rdata& source);
    //@}

private:
    RRParamRegistryImpl* impl_;
};

}
}
#endif  // __RRPARAMREGISTRY_H

// Local Variables: 
// mode: c++
// End: 
