// ball_scopedattribute.h                                             -*-C++-*-
#ifndef INCLUDED_BALL_SCOPEDATTRIBUTE
#define INCLUDED_BALL_SCOPEDATTRIBUTE

//@PURPOSE: Provide a scoped guard for a single BALL attribute.
//
//@CLASSES:
//  ball::ScopedAttribute: single attribute scoped guard
//
//@SEE_ALSO: ball_attribute
//
//@DESCRIPTION: This component defines a type, `ball::ScopedAttribute`, that
// serves as a scoped guard for `ball::Attribute` objects.  It defines a single
// attribute for the current thread while it is in scope.
//
// This component is used to associate an attribute (a name-value pair) with
// the current thread context for use when writing log records for the current
// thread.  This context information can both be written to the log itself, and
// used as input when evaluating whether a particular log should be written.
// For more information on how to use this feature, please see the package
// level documentation and usage examples for "Log Attributes" and "Rule-Based
// Logging".
//
///Usage
///-----
// This section illustrates intended use of this component.
//
///Example 1: Basic Usage of `ball::ScopedAttribute`
///- - - - - - - - - - - - - - - - - - - - - - - - -
// Suppose that service requests for a fictional service with id `999` are
// handled asynchronously by the function below.  Creating an instance of this
// class will set BALL attributes for any logging performed while the request
// is being processed:
// ```
// void handleServiceRequest(const Request& request)
// {
//    BALL_LOG_SET_CATEGORY("MY.SERVICE");
//
//    ball::ScopedAttribute attribute("request", request.selectionName());
//
//    BALL_LOG_TRACE << "Handling request: " << request;
//
//    // handle request here
// }
// ```
// Attribute "request" will be set in the calling thread and will affect
// publication of any BALL messages for the lifetime of `attribute`.

#include <balscm_version.h>

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

#include <bdlb_guid.h>

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

#include <bslmf_nestedtraitdeclaration.h>

#include <bsls_keyword.h>
#include <bsls_types.h>
#include <bsls_assert.h>

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

namespace BloombergLP {
namespace ball {

                    // ===============================
                    // class ScopedAttribute_Container
                    // ===============================

/// This component-private class is a concrete implementation of the
/// `AttributeContainer` protocol for a single attribute.
class ScopedAttribute_Container : public AttributeContainer {

    // DATA
    Attribute d_attribute;

    // NOT IMPLEMENTED
    ScopedAttribute_Container(const ScopedAttribute_Container&);
    ScopedAttribute_Container& operator=(const ScopedAttribute_Container&);

  public:
    // TYPES
    typedef bsl::allocator<char> allocator_type;

    // TRAITS
    BSLMF_NESTED_TRAIT_DECLARATION(ScopedAttribute_Container,
                                   bslma::UsesBslmaAllocator);

    // CREATORS

    /// Create a BALL attribute container holding a single rule, associating
    /// the specified `name` with the specified `value`.  Optionally specify
    /// an `allocator` (e.g., the address of a `bslma::Allocator` object) to
    /// supply memory; otherwise, the default allocator is used.
    ScopedAttribute_Container(
                        const char              *name,
                        const bsl::string_view&  value,
                        const allocator_type&    allocator = allocator_type());
    ScopedAttribute_Container(
                        const char              *name,
                        const char              *value,
                        const allocator_type&    allocator = allocator_type());
    ScopedAttribute_Container(
                        const char              *name,
                        int                      value,
                        const allocator_type&    allocator = allocator_type());
    ScopedAttribute_Container(
                        const char              *name,
                        long                     value,
                        const allocator_type&    allocator = allocator_type());
    ScopedAttribute_Container(
                        const char              *name,
                        long long                value,
                        const allocator_type&    allocator = allocator_type());
    ScopedAttribute_Container(
                        const char              *name,
                        unsigned int             value,
                        const allocator_type&    allocator = allocator_type());
    ScopedAttribute_Container(
                        const char              *name,
                        unsigned long            value,
                        const allocator_type&    allocator = allocator_type());
    ScopedAttribute_Container(
                        const char              *name,
                        unsigned long long       value,
                        const allocator_type&    allocator = allocator_type());
    ScopedAttribute_Container(
                        const char              *name,
                        bdlb::Guid               value,
                        const allocator_type&    allocator = allocator_type());
    ScopedAttribute_Container(
                        const char              *name,
                        const void              *value,
                        const allocator_type&    allocator = allocator_type());

    /// Destroy this object.
    ~ScopedAttribute_Container() BSLS_KEYWORD_OVERRIDE;

    // ACCESSORS

    /// Return `true` if the specified `attribute` is the same as the value
    /// held in this container, and `false` otherwise.
    bool hasValue(const Attribute& attribute) const BSLS_KEYWORD_OVERRIDE;

    /// 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.
    bsl::ostream& print(bsl::ostream& stream,
                        int           level = 0,
                        int           spacesPerLevel = 4)
                                                   const BSLS_KEYWORD_OVERRIDE;

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

                                  // Aspects

    /// 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.
    allocator_type get_allocator() const;
};

                         // =====================
                         // class ScopedAttribute
                         // =====================

/// This class provides a scoped guard that sets a single BALL attribute in
/// the current thread.
class ScopedAttribute {

    // DATA
    ScopedAttribute_Container        d_container;  // contains the attribute

    const AttributeContext::iterator d_it;         // reference to attribute
                                                   // container

    // NOT IMPLEMENTED
    ScopedAttribute(const ScopedAttribute&);
    ScopedAttribute& operator=(const ScopedAttribute&);

  public:
    // TYPES
    typedef bsl::allocator<char> allocator_type;

    // TRAITS
    BSLMF_NESTED_TRAIT_DECLARATION(ScopedAttribute,
                                   bslma::UsesBslmaAllocator);

    // CREATORS

    /// Set BALL logging attributes for the current thread for the scope of
    /// this object, associating the specified `name` with the specified
    /// `value`.  Optionally specify an `allocator` (e.g., the address of a
    /// `bslma::Allocator` object) to supply memory; otherwise, the default
    /// allocator is used.
    ScopedAttribute(const char              *name,
                    const bsl::string_view&  value,
                    const allocator_type&    allocator = allocator_type());
    ScopedAttribute(const char              *name,
                    const char              *value,
                    const allocator_type&    allocator = allocator_type());
    ScopedAttribute(const char              *name,
                    int                      value,
                    const allocator_type&    allocator = allocator_type());
    ScopedAttribute(const char              *name,
                    long                     value,
                    const allocator_type&    allocator = allocator_type());
    ScopedAttribute(const char              *name,
                    long long                value,
                    const allocator_type&    allocator = allocator_type());
    ScopedAttribute(const char              *name,
                    unsigned int             value,
                    const allocator_type&    allocator = allocator_type());
    ScopedAttribute(const char              *name,
                    unsigned long            value,
                    const allocator_type&    allocator = allocator_type());
    ScopedAttribute(const char              *name,
                    unsigned long long       value,
                    const allocator_type&    allocator = allocator_type());
    ScopedAttribute(const char              *name,
                    bdlb::Guid               value,
                    const allocator_type&    allocator = allocator_type());
    ScopedAttribute(const char              *name,
                    const void              *value,
                    const allocator_type&    allocator = allocator_type());

    /// Remove the attributes managed by this object from the BALL system,
    /// and destroy this object.
    ~ScopedAttribute();

                                  // Aspects

    /// 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.
    allocator_type get_allocator() const;
};

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

                    // -------------------------------
                    // class ScopedAttribute_Container
                    // -------------------------------

// CREATORS
inline
ScopedAttribute_Container::ScopedAttribute_Container(
                                            const char              *name,
                                            const bsl::string_view&  value,
                                            const allocator_type&    allocator)
: d_attribute(name, value, allocator)
{
}

inline
ScopedAttribute_Container::ScopedAttribute_Container(
                                            const char              *name,
                                            const char              *value,
                                            const allocator_type&    allocator)
: d_attribute(name, static_cast<bsl::string>(value), allocator)
{
}

inline
ScopedAttribute_Container::ScopedAttribute_Container(
                                             const char             *name,
                                             int                     value,
                                             const allocator_type&   allocator)
: d_attribute(name, value, allocator)
{
}

inline
ScopedAttribute_Container::ScopedAttribute_Container(
                                              const char            *name,
                                              long                   value,
                                              const allocator_type&  allocator)
: d_attribute(name, value, allocator)
{
}

inline
ScopedAttribute_Container::ScopedAttribute_Container(
                                              const char            *name,
                                              long long              value,
                                              const allocator_type&  allocator)
: d_attribute(name, value, allocator)
{
}

inline
ScopedAttribute_Container::ScopedAttribute_Container(
                                             const char             *name,
                                             unsigned int            value,
                                             const allocator_type&   allocator)
: d_attribute(name, value, allocator)
{
}

inline
ScopedAttribute_Container::ScopedAttribute_Container(
                                             const char             *name,
                                             unsigned long           value,
                                             const allocator_type&   allocator)
: d_attribute(name, value, allocator)
{
}

inline
ScopedAttribute_Container::ScopedAttribute_Container(
                                             const char             *name,
                                             unsigned long long      value,
                                             const allocator_type&   allocator)
: d_attribute(name, value, allocator)
{
}

inline
ScopedAttribute_Container::ScopedAttribute_Container(
                                             const char             *name,
                                             bdlb::Guid              value,
                                             const allocator_type&   allocator)
: d_attribute(name, value, allocator)
{
}

inline
ScopedAttribute_Container::ScopedAttribute_Container(
                                            const char              *name,
                                            const void              *value,
                                            const allocator_type&    allocator)
: d_attribute(name, value, allocator)
{
}

// ACCESSORS
inline
bool ScopedAttribute_Container::hasValue(const Attribute& attribute) const
{
    return d_attribute == attribute;
}

inline
void ScopedAttribute_Container::visitAttributes(
              const bsl::function<void(const ball::Attribute&)>& visitor) const
{
    visitor(d_attribute);
}

                                  // Aspects

inline
ScopedAttribute_Container::allocator_type
ScopedAttribute_Container::get_allocator() const
{
    return d_attribute.get_allocator();
}

                         // ---------------------
                         // class ScopedAttribute
                         // ---------------------

// CREATORS
inline
ScopedAttribute::ScopedAttribute(const char              *name,
                                 const bsl::string_view&  value,
                                 const allocator_type&    allocator)
: d_container(name, value, allocator)
, d_it(AttributeContext::getContext()->addAttributes(&d_container))
{
}

inline
ScopedAttribute::ScopedAttribute(const char              *name,
                                 const char              *value,
                                 const allocator_type&    allocator)
: d_container(name, value, allocator)
, d_it(AttributeContext::getContext()->addAttributes(&d_container))
{
}

inline
ScopedAttribute::ScopedAttribute(const char            *name,
                                 int                    value,
                                 const allocator_type&  allocator)
: d_container(name, value, allocator)
, d_it(AttributeContext::getContext()->addAttributes(&d_container))
{
}

inline
ScopedAttribute::ScopedAttribute(const char            *name,
                                 long                   value,
                                 const allocator_type&  allocator)
: d_container(name, value, allocator)
, d_it(AttributeContext::getContext()->addAttributes(&d_container))
{
}

inline
ScopedAttribute::ScopedAttribute(const char            *name,
                                 long long              value,
                                 const allocator_type&  allocator)
: d_container(name, value, allocator)
, d_it(AttributeContext::getContext()->addAttributes(&d_container))
{
}

inline
ScopedAttribute::ScopedAttribute(const char            *name,
                                 unsigned int           value,
                                 const allocator_type&  allocator)
: d_container(name, value, allocator)
, d_it(AttributeContext::getContext()->addAttributes(&d_container))
{
}

inline
ScopedAttribute::ScopedAttribute(const char            *name,
                                 unsigned long          value,
                                 const allocator_type&  allocator)
: d_container(name, value, allocator)
, d_it(AttributeContext::getContext()->addAttributes(&d_container))
{
}

inline
ScopedAttribute::ScopedAttribute(const char            *name,
                                 unsigned long long     value,
                                 const allocator_type&  allocator)
: d_container(name, value, allocator)
, d_it(AttributeContext::getContext()->addAttributes(&d_container))
{
}

inline
ScopedAttribute::ScopedAttribute(const char            *name,
                                 bdlb::Guid             value,
                                 const allocator_type&  allocator)
: d_container(name, value, allocator)
, d_it(AttributeContext::getContext()->addAttributes(&d_container))
{
}

inline
ScopedAttribute::ScopedAttribute(const char              *name,
                                 const void              *value,
                                 const allocator_type&    allocator)
: d_container(name, value, allocator)
, d_it(AttributeContext::getContext()->addAttributes(&d_container))
{
}

inline
ScopedAttribute::~ScopedAttribute()
{
    AttributeContext::getContext()->removeAttributes(d_it);
}

// ACCESSORS

                                  // Aspects

inline
ScopedAttribute::allocator_type
ScopedAttribute::get_allocator() const
{
    return d_container.get_allocator();
}

}  // close package namespace
}  // 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 ----------------------------------
