// balxml_namespaceregistry.h                                         -*-C++-*-
#ifndef INCLUDED_BALXML_NAMESPACEREGISTRY
#define INCLUDED_BALXML_NAMESPACEREGISTRY

#include <bsls_ident.h>
BSLS_IDENT("$Id: $")

//@PURPOSE: Provide a unique integer ID for each XML namespace.
//
//@CLASSES:
//   balxml::NamespaceRegistry: namespace-to-id mapping registry
//
//@SEE_ALSO: balxml_prefixstack
//
//@DESCRIPTION: This component provides an in-core value-semantic type,
// 'balxml::NamespaceRegistry', that associates an integer ID with each
// registered namespace URI.  In typical usage, client code would call the
// 'lookupOrRegister' method each time it encounters a namespace URI.  The
// 'lookupOrRegister' method will return the ID corresponding to the URI,
// assigning a new ID if none already exists.  The client can also retrieve
// the ID an already-registered namespace by providing the URI to the 'lookup'
// method and can retrieve the URI of an already-registered namespace by
// providing the ID to the 'lookup' method.
//
// Note that namespace IDs may be negative.  Client code should not assume an
// incremental assignment of IDs starting at zero.  (See Preregistered
// Namespaces), below.
//
///Preregistered Namespaces
///------------------------
// Even before any namespaces have been registered, a
// 'balxml::NamespaceRegistry' can be used to lookup several preregistered
// namespaces.  The IDs for these preregistered namespaces are declared as
// constants within the 'balxml::NamespaceRegistry' class.  These constants and
// their associated URI's are as follows:
//..
//  Namespace ID               URI String
//  ============               ==========
//  BAEXML_XML                   http://www.w3.org/XML/1998/namespace
//  BAEXML_XMLNS                 http://www.w3.org/2000/xmlns/
//  BAEXML_XMLSCHEMA             http://www.w3.org/2001/XMLSchema
//  BAEXML_XMLSCHEMA_INSTANCE    http://www.w3.org/2001/XMLSchema-instance
//  BAEXML_WSDL                  http://schemas.xmlsoap.org/wsdl/
//  BAEXML_WSDL_SOAP             http://schemas.xmlsoap.org/wsdl/soap/
//  BAEXML_BDEM                  http://bloomberg.com/schemas/bdem
//..
// Note that the above constants are negative numbers.  In addition, the
// value, -1, is permanently assigned to the empty string.  The use of
// predefined namespace IDs allows client code avoid lookups of the above,
// well-known URIs.
//
///Thread Safety
///-------------
// It is safe to read or modify multiple instances of
// 'balxml::NamespaceRegistry' simultaneously, each from a separate thread.  It
// is safe to read a single instance of 'balxml::NamespaceRegistry' from
// multiple threads, provided no thread is modifying it at the same time.  It
// is not safe to read or modify an instance of 'balxml::NamespaceRegistry'
// from one thread while any other thread is modifying the same instance.
//
///Usage
///-----
// Typically, a program will register namespaces as it encounters them in an
// XML document.  Alternatively, namespaces that are important to the program
// are registered in advance, as in the following code:
//..
//  const char googleUri[] = "http://www.google.com/schemas/results.xsd";
//  const char yahooUri[]  = "http://www.yahoo.com/xsd/searchResults.xsd";
//
//  balxml::NamespaceRegistry namespaceRegistry;
//  int googleId = namespaceRegistry.lookupOrRegister(googleUri);
//  assert(googleId >= 0);
//  int yahooId = namespaceRegistry.lookupOrRegister(yahooUri);
//  assert(yahooId >= 0);
//  assert(yahooId != googleId);
//..
// Later, IDs can be looked up without concern for whether they have already
// been registered.  Any new namespaces are simply given a new ID:
//..
//  char input[100];
//
//  // First input is a new namespace URI.
//  bsl::strcpy(input, "http://www.bloomberg.com/schemas/example.xsd");
//  int id1 = namespaceRegistry.lookupOrRegister(input);
//  assert(id1 >= 0);
//  assert(id1 != googleId);
//  assert(id1 != yahooId);
//
//  // Next input happens to be the same as yahoo.
//  bsl::strcpy(input, "http://www.yahoo.com/xsd/searchResults.xsd");
//  int id2 = namespaceRegistry.lookupOrRegister(input);
//  assert(id2 == yahooId);
//..
// If one of the preregistered namespaces is presented, it's predefined ID is
// returned, even though it was never explicitly registered:
//..
//  bsl::strcpy(input, "http://www.w3.org/2001/XMLSchema");
//  int id3 = namespaceRegistry.lookupOrRegister(input);
//  assert(id3 == balxml::NamespaceRegistry::BAEXML_XMLSCHEMA);
//..
// Using the 'lookup' method, a namespace ID can be looked up without
// registering it.  In this case, an unregistered namespace will result in an
// ID of -1:
//..
//  assert(googleId  == namespaceRegistry.lookup(googleUri));
//  assert(balxml::NamespaceRegistry::BAEXML_BDEM ==
//         namespaceRegistry.lookup("http://bloomberg.com/schemas/bdem"));
//  assert(-1 == namespaceRegistry.lookup("urn:1234"));
//..
// There is also a 'lookup' method for performing the reverse mapping -- from
// ID to URI:
//..
//  const char *uri = namespaceRegistry.lookup(googleId);
//  assert(0 == bsl::strcmp(uri, googleUri));
//..

#include <balscm_version.h>

#include <bslma_allocator.h>
#include <bslma_usesbslmaallocator.h>

#include <bslmf_nestedtraitdeclaration.h>

#include <bsl_string.h>
#include <bsl_vector.h>
#include <bsl_iosfwd.h>

namespace BloombergLP {

namespace balxml {
                          // =======================
                          // class NamespaceRegistry
                          // =======================

class NamespaceRegistry {
    // Mapping that associates a unique integer with each registered namespace
    // URI.

  private:
    // PRIVATE MEMBER VARIABLES
    bsl::vector<bsl::string> d_namespaces; // vector of namespaces, indexed by
                                           // the namespace ID.
    friend inline
    bool operator==(const NamespaceRegistry& lhs,
                    const NamespaceRegistry& rhs);
        // Must be a friend for engineering reasons.  Unlike most
        // value-semantic types, there is no efficient way to read the entire
        // value of a namespace registry object.

  public:
    // TRAITS
    BSLMF_NESTED_TRAIT_DECLARATION(NamespaceRegistry,
                                                    bslma::UsesBslmaAllocator);

    // PUBLIC TYPES
    enum {
        // Preregistered namespace IDs.
        e_NO_NAMESPACE = -1,    // (empty URI string)

        e_PREDEF_MIN = 0x40000000,

        e_XML = e_PREDEF_MIN, // http://www.w3.org/XML/1998/namespace
        e_XMLNS,              // http://www.w3.org/2000/xmlns/
        e_XMLSCHEMA,          // http://www.w3.org/2001/XMLSchema
        e_XMLSCHEMA_INSTANCE, // http://www.w3.org/2001/XMLSchema-instance
        e_WSDL,               // http://schemas.xmlsoap.org/wsdl/
        e_WSDL_SOAP,          // http://schemas.xmlsoap.org/wsdl/soap/
        e_BDEM,               // http://bloomberg.com/schemas/bdem

        BAEXML_PREDEF_MAX
#ifndef BDE_OMIT_INTERNAL_DEPRECATED
      , BAEXML_NO_NAMESPACE = e_NO_NAMESPACE
      , BAEXML_PREDEF_MIN = e_PREDEF_MIN
      , BAEXML_XML = e_XML
      , BAEXML_XMLNS = e_XMLNS
      , BAEXML_XMLSCHEMA = e_XMLSCHEMA
      , BAEXML_XMLSCHEMA_INSTANCE = e_XMLSCHEMA_INSTANCE
      , BAEXML_WSDL = e_WSDL
      , BAEXML_WSDL_SOAP = e_WSDL_SOAP
      , BAEXML_BDEM = e_BDEM
      , NSID_NO_NAMESPACE       = e_NO_NAMESPACE
      , NSID_PREDEF_MIN         = e_PREDEF_MIN
      , NSID_XML                = e_XML
      , NSID_XMLNS              = e_XMLNS
      , NSID_XMLSCHEMA          = e_XMLSCHEMA
      , NSID_XMLSCHEMA_INSTANCE = e_XMLSCHEMA_INSTANCE
      , NSID_WSDL               = e_WSDL
      , NSID_WSDL_SOAP          = e_WSDL_SOAP
      , NSID_BDEM               = e_BDEM
      , NSID_PREDEF_MAX         = BAEXML_PREDEF_MAX
#endif // BDE_OMIT_INTERNAL_DEPRECATED
    };

    // CREATORS
    NamespaceRegistry(bslma::Allocator *basicAllocator = 0);
        // Construct an empty registry.  Optionally specify a 'basicAllocator'
        // used to supply memory.  If 'basicAllocator' is 0, the current
        // default allocator is used.

    NamespaceRegistry(const NamespaceRegistry&  other,
                      bslma::Allocator         *basicAllocator=0);
        // Construct a copy of the specified 'other' namespace registry using
        // the (optionally) specified 'basicAllocator'.  For a given URI, the
        // results of calling 'lookup' by URI will produce equal results for
        // this object and for 'other'.  For a given integer ID, the result of
        // calling 'lookup' by ID will produce different pointers that compare
        // equal using 'strcmp'.

    ~NamespaceRegistry();
        // Destroy this object.  Release all memory to the allocator used at
        // construction.

    // MANIPULATORS
    NamespaceRegistry& operator=(const NamespaceRegistry& rhs);
        // Discard the contents of this registry and assign it the contents of
        // the specified 'rhs' registry.  For a given URI, the results of
        // calling 'lookup' by URI will produce equal results for this object
        // and for 'rhs'.  For a given integer ID, the result of calling
        // 'lookup' by ID will produce different pointers that compare equal
        // using 'strcmp'.

    int lookupOrRegister(const bsl::string_view& namespaceUri);
        // Return the integer ID for the specified 'namespaceUri', assigning a
        // new ID if the 'namespaceUri' has not been registered before.  Note
        // that the IDs for pre-registered namespaces (including the empty
        // URI) are less than zero.  (See "Preregistered Namespaces" in the
        // 'balxml_namespaceregistry component-level documentation.)

    void reset();
        // Removes all registered namespaces.  Preregistered namespaces are
        // not removed.

    // ACCESSORS
    int lookup(const bsl::string_view& namespaceUri) const;
        // Return the integer ID for the specified 'namespaceUri' or -1 if the
        // namespace has not been registered.  Note that not all negative
        // return values correspond to unregistered namespaces.  Preregistered
        // namespaces always have negative IDs.  (See "Preregistered
        // Namespaces" in the 'balxml_namespaceregistry' component-level
        // documentation.)  Note that a return value of -1 can mean either an
        // unregistered namespace or an empty URI string.  This dual-use of -1
        // is deliberate and simplifies error handling in most clients.

    const char *lookup(int id) const;
        // Return the null-terminated string containing the URI of the
        // namespace registered with the specified 'id' or an empty (not null)
        // string if 'id' does not correspond to a preregistered namespace or
        // a namespace that was previously registered with this object.

    void print(bsl::ostream& stream) const;
        // Print the contents of this object to the specified 'stream' in
        // human-readable form.
};

// ============================================================================
//                            INLINE DEFINITIONS
// ============================================================================

// FREE OPERATORS
inline
bool operator==(const NamespaceRegistry& lhs, const NamespaceRegistry& rhs);
    // Return true if the specified 'lhs' registry has the same value as the
    // specified 'rhs' registry and false otherwise.  The two registries have
    // the same value if, for any possible URI string, 'u',
    // 'lhs.lookup(u) == rhs.lookup(u)'.

inline
bool operator!=(const NamespaceRegistry& lhs, const NamespaceRegistry& rhs);
    // Return true if the specified 'lhs' registry does not have the same
    // value as the specified 'rhs' registry and false otherwise.  The two
    // registries do not have the same value if there exists a URI string,
    // 'u', such that 'lhs.lookup(u)' != 'rhs.lookup(u)'.

inline
bsl::ostream& operator<<(bsl::ostream& os, const NamespaceRegistry& r);
    // Print the contents of the specified 'r' registry to the specified 'os'
    // stream in human-readable form and return a modifiable reference to
    // 'os'.

// CREATORS
inline
NamespaceRegistry::NamespaceRegistry(bslma::Allocator *basicAllocator)
: d_namespaces(basicAllocator)
{
}

inline
NamespaceRegistry::NamespaceRegistry(const NamespaceRegistry&  other,
                                     bslma::Allocator         *basicAllocator)
: d_namespaces(other.d_namespaces, basicAllocator)
{
}

inline
NamespaceRegistry::~NamespaceRegistry()
{
}

// MANIPULATORS
inline
NamespaceRegistry&
NamespaceRegistry::operator=(const NamespaceRegistry& rhs)
{
    d_namespaces = rhs.d_namespaces;
    return *this;
}

inline
void NamespaceRegistry::reset()
{
    d_namespaces.clear();
}
}  // close package namespace

// FREE OPERATORS
inline
bool balxml::operator==(const NamespaceRegistry& lhs,
                        const NamespaceRegistry& rhs)
{
    return lhs.d_namespaces == rhs.d_namespaces;
}

inline
bool balxml::operator!=(const NamespaceRegistry& lhs,
                        const NamespaceRegistry& rhs)
{
    return ! (lhs == rhs);
}

inline
bsl::ostream& balxml::operator<<(bsl::ostream& os, const NamespaceRegistry& r)
{
    r.print(os);
    return os;
}

}  // close enterprise namespace

#endif // ! defined(INCLUDED_BAEXML_NAMESPACEREGISTRY)

// ----------------------------------------------------------------------------
// Copyright 2015 Bloomberg Finance L.P.
//
// Licensed 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.
// ----------------------------- END-OF-FILE ----------------------------------