// ball_defaultattributecontainer.h                                   -*-C++-*-
#ifndef INCLUDED_BALL_DEFAULTATTRIBUTECONTAINER
#define INCLUDED_BALL_DEFAULTATTRIBUTECONTAINER

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

//@PURPOSE: Provide a default container for storing attribute name/value pairs.
//
//@CLASSES:
//  ball::DefaultAttributeContainer: a collection of unique attributes
//
//@SEE_ALSO: ball_attributecontainer
//
//@DESCRIPTION: This component provides a default implementation of the
// 'ball::AttributeContainer' protocol, 'ball::DefaultAttributeContainer'
// providing an 'unordered_set'-based container of 'ball::Attribute' values.
// Each attribute within the default attribute container holds a
// (case-sensitive) name and a value, which may be an 'int', a 64-bit integer,
// or a 'bsl::string'.
//
// This component participates in the implementation of "Rule-Based Logging".
// For more information on how to use that feature, please see the package
// level documentation and usage examples for "Rule-Based Logging".
//
///Thread Safety
///-------------
// 'ball::DefaultAttributeContainer' is *const* *thread-safe*, meaning that
// accessors may be invoked concurrently from different threads, but it is not
// safe to access or modify a 'ball::DefaultAttributeContainer' in one thread
// while another thread modifies the same object.
//
///Usage
///-----
// This section illustrates the intended use of this component.
//
///Example 1: Basic Usage of 'ball::DefaultAttributeContainer'
///- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// A 'ball::DefaultAttributeContainer' initially has no attributes when created
// by the default constructor:
//..
//    ball::DefaultAttributeContainer attributeContainer;
//..
// Let's now create some attributes and add them to the attribute map:
//..
//    ball::Attribute a1("uuid", 1111);
//    ball::Attribute a2("sid", "111-1");
//    assert(true == attributeContainer.addAttribute(a1));
//    assert(true == attributeContainer.addAttribute(a2));
//..
// New attributes with a name that already exists in the map can be added, as
// long as they have a different value:
//..
//    ball::Attribute a3("uuid", 2222);
//    ball::Attribute a4("sid", "222-2");
//    assert(true == attributeContainer.addAttribute(a3));
//    assert(true == attributeContainer.addAttribute(a4));
//..
// But attributes having the same name and value cannot be added:
//..
//    ball::Attribute a5("uuid", 1111);                 // same as 'a1'
//    assert(false == attributeContainer.addAttribute(a5));
//..
// Note that the attribute name is case-sensitive:
//..
//    ball::Attribute a6("UUID", 1111);
//    assert(true == attributeContainer.addAttribute(a6));
//..
// Existing attributes can be looked up by the 'hasValue' method:
//..
//    assert(true == attributeContainer.hasValue(a1));
//    assert(true == attributeContainer.hasValue(a2));
//    assert(true == attributeContainer.hasValue(a3));
//    assert(true == attributeContainer.hasValue(a4));
//    assert(true == attributeContainer.hasValue(a5));
//    assert(true == attributeContainer.hasValue(a6));
//..
// Or removed by the 'removeAttribute' method:
//..
//    defaultattributecontainer.removeAttribute(a1);
//    assert(false == attributeContainer.hasValue(a1));
//..
// Also, the 'ball::DefaultAttributeContainer' class provides an iterator:
//..
//    ball::DefaultAttributeContainer::const_iterator iter =
//                                                  attributeContainer.begin();
//    for ( ; iter != attributeContainer.end(); ++iter ) {
//        bsl::cout << *iter << bsl::endl;
//    }
//..
// Finally, we can provide a visitor functor and visit all attributes in the
// container.  Note that this usage example uses lambdas and requires C++11.
// Lambdas can be replaced with named functions for C++03.
//..
//    bsl::vector<ball::Attribute> result;
//    attributeContainer.visitAttributes(
//        [&result](const ball::Attribute& attribute)
//          {
//              result.push_back(attribute);
//          });
//    assert(4 == result.size());
//..

#include <balscm_version.h>

#include <ball_attribute.h>
#include <ball_attributecontainer.h>

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

#include <bslmf_nestedtraitdeclaration.h>

#include <bsl_functional.h>
#include <bsl_unordered_set.h>

namespace BloombergLP {
namespace ball {

                    // ===============================
                    // class DefaultAttributeContainer
                    // ===============================

class DefaultAttributeContainer : public AttributeContainer {
    // A 'DefaultAttributeContainer' object contains a collection of (unique)
    // attributes values.

    // PRIVATE TYPES
    struct AttributeHash {
        // A hash functor for 'Attribute'.

      private:
        // CLASS DATA
        static int s_hashtableSize;  // default hashtable size for which the
                                     // hash value is calculated
      public:
        // ACCESSORS
        int operator()(const Attribute& attribute) const
            // Return the hash value of the specified 'attribute'.
        {
            return Attribute::hash(attribute, s_hashtableSize);
        }
    };

    // CLASS DATA
    static int                   s_initialSize;   // initial size of the
                                                  // attribute map

    // DATA
    bsl::unordered_set<Attribute, AttributeHash>
                                 d_attributeSet;  // hash table that stores
                                                  // all the attributes
                                                  // managed by this object

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

    // TYPES
    typedef bsl::allocator<char> allocator_type;
        // This 'typedef' is an alias for the allocator used by this object.

    typedef bsl::unordered_set<Attribute, AttributeHash>::const_iterator
                                 const_iterator;  // type of iterator for
                                                  // iterating through the
                                                  // non-modifiable attributes
                                                  // managed by this object

    // CREATORS
    DefaultAttributeContainer();
    explicit DefaultAttributeContainer(const allocator_type& allocator);
        // Create an empty 'DefaultAttributeContainer' object.  Optionally
        // specify an 'allocator' (e.g., the address of a 'bslma::Allocator'
        // object) to supply memory; otherwise, the default allocator is used.

    DefaultAttributeContainer(
               const DefaultAttributeContainer&  original,
               const allocator_type&             allocator = allocator_type());
        // Create a 'DefaultAttributeContainer' object having the same value as
        // the specified 'original' object.  Optionally specify an 'allocator'
        // (e.g., the address of a 'bslma::Allocator' object) to supply memory;
        // otherwise, the default allocator is used.

    virtual ~DefaultAttributeContainer();
        // Destroy this object.

    // MANIPULATORS
    DefaultAttributeContainer& operator=(const DefaultAttributeContainer& rhs);
        // Assign to this object the value of the specified 'rhs' object, and
        // return a non-'const' reference to this object.

    bool addAttribute(const Attribute& value);
        // Add an attribute having the specified 'value' to this object.
        // Return 'true' on success and 'false' if an attribute having the
        // same 'value' already exists in this object.

    bool removeAttribute(const Attribute& value);
        // Remove the attribute having the specified 'value' from this object.
        // Return the 'true' on success and 'false' if the attribute having the
        // 'value' does not exist in this object.

    void removeAllAttributes();
        // Remove every attribute in this attribute set.

    // ACCESSORS
    int numAttributes() const;
        // Return the number of attributes managed by this object.

    virtual bool hasValue(const Attribute& value) const;
        // Return 'true' if the attribute having specified 'value' exists in
        // this object, and 'false' otherwise.

    const_iterator begin() const;
        // Return an iterator pointing at the beginning of the (unordered)
        // sequence of attributes managed by this map, or 'end()' if
        // 'numAttributes()' is 0.

    const_iterator end() const;
        // Return an iterator pointing at one past the end of the map.

    virtual bsl::ostream& print(bsl::ostream& stream,
                                int           level = 0,
                                int           spacesPerLevel = 4) const;
        // Format this object to the specified output 'stream' at the (absolute
        // value of) the optionally specified indentation 'level' and return a
        // reference to 'stream'.  If 'level' is specified, optionally specify
        // 'spacesPerLevel', the number of spaces per indentation level for
        // this and all of its nested objects.  If 'level' is negative,
        // suppress indentation of the first line.  If 'spacesPerLevel' is
        // negative, format the entire output on one line, suppressing all but
        // the initial indentation (as governed by 'level').  If 'stream' is
        // not valid on entry, this operation has no effect.

    virtual void visitAttributes(
             const bsl::function<void(const ball::Attribute&)> &visitor) const;
        // Invoke the specified 'visitor' function for all attributes in this
        // container.

                                  // Aspects

    allocator_type get_allocator() const;
        // Return the allocator used by this object to supply memory.  Note
        // that if no allocator was supplied at construction the default
        // allocator in effect at construction is used.

};

// FREE OPERATORS
bool operator==(const DefaultAttributeContainer& lhs,
                const DefaultAttributeContainer& rhs);
    // Return 'true' if the specified 'lhs' and 'rhs' objects have the same
    // value, and 'false' otherwise.  Two 'DefaultAttributeContainer' objects
    // have the same value if they contain the same number of (unique)
    // attributes, and every attribute that appears in one object also appears
    // in the other.

bool operator!=(const DefaultAttributeContainer& lhs,
                const DefaultAttributeContainer& rhs);
    // Return 'true' if the specified 'lhs' and 'rhs' objects do not have the
    // same value, and 'false' otherwise.  Two 'DefaultAttributeContainer'
    // objects do not have the same value if they contain differing numbers of
    // attributes or if there is at least one attribute that appears in one
    // object, but not in the other.

bsl::ostream& operator<<(bsl::ostream&                    output,
                         const DefaultAttributeContainer& attributeContainer);
    // Write the value of the specified 'attributeContainer' to the specified
    // 'output' stream in some single-line, human readable format.  Return the
    // 'output' stream.

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

                    // -------------------------------
                    // class DefaultAttributeContainer
                    // -------------------------------

// CREATORS
inline
DefaultAttributeContainer::DefaultAttributeContainer()
: d_attributeSet(s_initialSize,                    // initial size
                 AttributeHash(),                  // hash functor
                 bsl::equal_to<Attribute>(),       // equal functor
                 allocator_type())
{
}

inline
DefaultAttributeContainer::DefaultAttributeContainer(
                                               const allocator_type& allocator)
: d_attributeSet(s_initialSize,                    // initial size
                 AttributeHash(),                  // hash functor
                 bsl::equal_to<Attribute>(),       // equal functor
                 allocator)
{
}

inline
DefaultAttributeContainer::DefaultAttributeContainer(
                                   const DefaultAttributeContainer&  original,
                                   const allocator_type&             allocator)
: d_attributeSet(original.d_attributeSet, allocator)
{
}

inline
DefaultAttributeContainer::~DefaultAttributeContainer()
{
}

// MANIPULATORS
inline
bool DefaultAttributeContainer::addAttribute(const Attribute& value)
{
    return d_attributeSet.insert(value).second;
}

inline
bool DefaultAttributeContainer::removeAttribute(const Attribute& value)
{
    return d_attributeSet.erase(value) != 0;
}

inline
void DefaultAttributeContainer::removeAllAttributes()
{
    d_attributeSet.clear();
}

// ACCESSORS
inline
int DefaultAttributeContainer::numAttributes() const
{
    return static_cast<int>(d_attributeSet.size());
}

inline
DefaultAttributeContainer::const_iterator
DefaultAttributeContainer::begin() const
{
    return d_attributeSet.begin();
}

inline
DefaultAttributeContainer::const_iterator
DefaultAttributeContainer::end() const
{
    return d_attributeSet.end();
}

                                  // Aspects

inline
DefaultAttributeContainer::allocator_type
DefaultAttributeContainer::get_allocator() const
{
    return d_attributeSet.get_allocator();
}

}  // close package namespace

// FREE OPERATORS
inline
bsl::ostream& ball::operator<<(
                           bsl::ostream&                    output,
                           const DefaultAttributeContainer& attributeContainer)
{
    return attributeContainer.print(output, 0, -1);
}

}  // close enterprise namespace

#endif

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