// ball_attribute.h                                                   -*-C++-*-
#ifndef INCLUDED_BALL_ATTRIBUTE
#define INCLUDED_BALL_ATTRIBUTE

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

//@PURPOSE: Provide a representation of (literal) name/value pairs.
//
//@CLASSES:
//  ball::Attribute: (literal) name/value pair
//
//@SEE_ALSO: ball_managedattribute
//
//@DESCRIPTION: This component implements an unusual in-core value-semantic
// class, 'ball::Attribute'.  Each instance of this type represents an
// attribute that consists of a (literal) name (held but not owned), and an
// associated value (owned) that can be an 'int', 'long', 'long long',
// 'unsigned int', unsigned long', 'unsigned long long', 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".
//
// IMPORTANT: The attribute name, whose type is 'const char *', must therefore
// remain valid throughout the life time of the 'ball::Attribute' object and
// that of any other 'ball::Attribute' objects that are copied or assigned from
// the original object.  It is recommended that only null-terminated C-string
// literals be used for names.
//
///Attribute Naming Recommendations
///--------------------------------
// Attributes can be rendered as part of a log message and used for log
// post-processing and analysis.  It is recommended to use the following naming
// conventions for attribute names:
//
//: o An attribute name should start with an alphabetic character, no other
//:   special characters, digits should be allowed as the first character of
//:   the attribute name.
//: o An attribute name should not contain whitespaces.
//: o An attribute name should contain only alphanumeric characters,
//:   underscores('_'), and dots('.').  Do not use any other special
//:   characters.
//
// Disregarding these conventions may prevent the log output from being
// correctly parsed by commonly used log processing software.
//
///Usage
///-----
// This section illustrates intended use of this component.
//
///Example 1: Basic 'Attribute' usage
/// - - - - - - - - - - - - - - - - -
// The following code creates four attributes having the same name, but
// different attribute value types.
//..
//    ball::Attribute a1("day", "Sunday");
//    ball::Attribute a2("day", 7);
//    ball::Attribute a3("day", 7LL);
//    ball::Attribute a4("day", 7ULL);
//..
// The names of the attributes can be found by calling the 'name' method:
//..
//    assert(0 == bsl::strcmp("day", a1.name()));
//    assert(0 == bsl::strcmp("day", a2.name()));
//    assert(0 == bsl::strcmp("day", a3.name()));
//    assert(0 == bsl::strcmp("day", a4.name()));
//..
// The 'value' method returns a non-modifiable reference to the
// 'bdlb::Variant' object that manages the value of the attribute:
//..
//    assert(true     == a1.value().is<bsl::string>());
//    assert("Sunday" == a1.value().the<bsl::string>());
//
//    assert(true     == a2.value().is<int>());
//    assert(7        == a2.value().the<int>());
//
//    assert(true     == a3.value().is<long long>());
//    assert(7        == a3.value().the<long long>());
//
//    assert(true     == a4.value().is<unsigned long long>());
//    assert(7        == a4.value().the<unsigned long long>());
//..
// Note that the name string that is passed to the constructor of
// 'ball::Attribute' *must* remain valid and unchanged after the
// 'ball::Attribute' object is created.  In the next example, we create a
// temporary buffer to store the name string, and then use the buffer to
// create an attribute.  Note that any subsequent changes to this temporary
// buffer will also modify the name of the attribute:
//..
//    char buffer[] = "Hello";
//    ball::Attribute a4(buffer, 1);                   // BAD IDEA!!!
//    bsl::strcpy(buffer, "World");
//    assert(0 == bsl::strcmp("World", a4.name()));
//..
// The 'ball::Attribute' class also provides a constructor that takes a value
// of type 'ball::Attribute::Value':
//..
//    ball::Attribute::Value value;
//    value.assign<bsl::string>("Sunday");
//    ball::Attribute a5("day", value);
//    assert(a5 == a1);
//..
//
///Example 2: Using 'Attribute' to log pointers to opaque structure
/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Consider we have an event scheduler that operates on events referred to by
// event handle:
//..
//    struct Event {
//        d_int d_id;
//    };
//
//    typedef Event * EventHandle;
//..
// The event handler value can be logged using 'ball::Attribute' as follows:
//..
//    Event           event;
//    EventHandle     handle = &event;
//    ball::Attribute a7("event", handle);
//
//    assert(true   == a7.value().is<const void *>());
//    assert(handle == a7.value().the<const void *>());
//..

#include <balscm_version.h>

#include <bdlb_variant.h>

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

#include <bslmf_nestedtraitdeclaration.h>

#include <bsls_assert.h>

#include <bsl_cstring.h>
#include <bsl_string.h>

namespace BloombergLP {
namespace ball {

                        // ===============
                        // class Attribute
                        // ===============

class Attribute {
    // An 'Attribute' object contains an attribute name which is not managed
    // and an attribute value which is managed.

  public:
    // TYPES
    typedef bdlb::Variant<int,
                          long,
                          long long,
                          unsigned int,
                          unsigned long,
                          unsigned long long,
                          bsl::string,
                          const void *> Value;
        // 'Value' is an alias for the attribute type variant.

  private:
    // DATA
    const char  *d_name;       // attribute name

    Value        d_value;      // attribute value

    mutable int  d_hashValue;  // hash value (-1 indicates it is unset)

    mutable int  d_hashSize;   // hash size from which the hash value was
                               // calculated (0 indicates hash value is unset)

    // FRIENDS
    friend bool operator==(const Attribute&, const Attribute&);
    friend bool operator!=(const Attribute&, const Attribute&);
    friend bsl::ostream& operator<<(bsl::ostream&, const Attribute&);

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

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

    // CLASS METHODS
    static int hash(const Attribute& attribute, int size);
        // Return a hash value calculated from the specified 'attribute' using
        // the specified 'size' as the number of slots.  The hash value is
        // guaranteed to be in the range '[0 .. size - 1]'.  The behavior is
        // undefined unless '0 < size'.

    // CREATORS
    Attribute(const char              *name,
              const bsl::string_view&  value,
              const allocator_type&    allocator = allocator_type());
        // Create an 'Attribute' object having the specified (literal) 'name'
        // and (character string) 'value'.  Optionally specify an 'allocator'
        // (e.g., the address of a 'bslma::Allocator' object) to supply memory;
        // otherwise, the default allocator is used.  Note that 'name' is not
        // managed by this object and therefore must remain valid while in use
        // by any 'Attribute' object.

    Attribute(const char            *name,
              const char            *value,
              const allocator_type&  allocator = allocator_type());
        // Create an 'Attribute' object having the specified (literal) 'name'
        // and (character string) 'value'.  Optionally specify an 'allocator'
        // (e.g., the address of a 'bslma::Allocator' object) to supply memory;
        // otherwise, the default allocator is used.  Note that 'name' is not
        // managed by this object and therefore must remain valid while in use
        // by any 'Attribute' object.

    Attribute(const char            *name,
              int                    value,
              const allocator_type&  allocator = allocator_type());
    Attribute(const char            *name,
              long                   value,
              const allocator_type&  allocator = allocator_type());
    Attribute(const char            *name,
              long long              value,
              const allocator_type&  allocator = allocator_type());
    Attribute(const char            *name,
              unsigned int           value,
              const allocator_type&  allocator = allocator_type());
    Attribute(const char            *name,
              unsigned long          value,
              const allocator_type&  allocator = allocator_type());
    Attribute(const char            *name,
              unsigned long long     value,
              const allocator_type&  allocator = allocator_type());
        // Create an 'Attribute' object having the specified (literal) 'name'
        // and 'value'.  Optionally specify an 'allocator' (e.g., the address
        // of a 'bslma::Allocator' object) to supply memory; otherwise, the
        // default allocator is used.  Note that 'name' is not managed by this
        // object and therefore must remain valid while in use by any
        // 'Attribute' object.

    Attribute(const char            *name,
              const void            *value,
              const allocator_type&  allocator = allocator_type());
        // Create an 'Attribute' object having the specified (literal) 'name'
        // and (const-qualified void pointer) 'value'.  Optionally specify an
        // 'allocator' (e.g., the address of a 'bslma::Allocator' object) to
        // supply memory; otherwise, the default allocator is used.

    Attribute(const char            *name,
              const Value&           value,
              const allocator_type&  allocator = allocator_type());
        // Create an 'Attribute' object having the specified (literal) 'name'
        // and 'value'.  Optionally specify an 'allocator' (e.g., the address
        // of a 'bslma::Allocator' object) to supply memory; otherwise, the
        // default allocator is used.  Note that 'name' is not managed by this
        // object and therefore must remain valid while in use by any
        // 'Attribute' object.

    Attribute(const Attribute&      original,
              const allocator_type& allocator = allocator_type());
        // Create an 'Attribute' object having the same (literal) name and
        // attribute 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.

    //! ~Attribute() = default;
        // Destroy this attribute object.

    // MANIPULATORS
    Attribute& operator=(const Attribute& rhs);
        // Assign the value of the specified 'rhs' object to this object.

    void setName(const char *name);
        // Set the attribute name of this object to the specified (literal)
        // 'name'.  Note that 'name' is not managed by this object and
        // therefore must remain valid while in use by any 'Attribute' object.

    void setValue(const Value&             value);
    void setValue(int                      value);
    void setValue(long                     value);
    void setValue(long long                value);
    void setValue(unsigned int             value);
    void setValue(unsigned long            value);
    void setValue(unsigned long long       value);
    void setValue(const bsl::string_view&  value);
    void setValue(const char              *value);
    void setValue(const void              *value);
        // Set the attribute value of this object to the specified 'value'.

    // ACCESSORS
    const char *name() const;
        // Return the name of this object.

    const Value& value() const;
        // Return a reference to the non-modifiable attribute value of this
        // object.

    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.

                                  // 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 Attribute& lhs, const Attribute& rhs);
    // Return 'true' if the specified 'lhs' and 'rhs' objects have the same
    // value, and 'false' otherwise.  Two 'Attribute' objects have the same
    // value if they have the same name (but not necessarily the identical
    // representation in memory), the same attribute value type, and the same
    // attribute value.

bool operator!=(const Attribute& lhs, const Attribute& rhs);
    // Return 'true' if the specified 'lhs' and 'rhs' objects do not have the
    // same value, and 'false' otherwise.  Two 'Attribute' objects do not have
    // the same value if any of their respective names (value, not address),
    // attribute value types, or attribute values differ.

bsl::ostream& operator<<(bsl::ostream& output, const Attribute& attribute);
    // Write the value of the specified 'attribute' to the specified 'output'
    // stream.  Return the specified 'output' stream.

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

                        // ---------------
                        // class Attribute
                        // ---------------

// CREATORS
inline
Attribute::Attribute(const char              *name,
                     const bsl::string_view&  value,
                     const allocator_type&    allocator)
: d_name(name)
, d_value(allocator.mechanism())
, d_hashValue(-1)
, d_hashSize(0)
{
    d_value.assign<bsl::string>(bsl::string(value));
}

inline
Attribute::Attribute(const char            *name,
                     const char            *value,
                     const allocator_type&  allocator)
: d_name(name)
, d_value(allocator.mechanism())
, d_hashValue(-1)
, d_hashSize(0)
{
    d_value.assign<bsl::string>(value);
}

inline
Attribute::Attribute(const char            *name,
                     int                    value,
                     const allocator_type&  allocator)
: d_name(name)
, d_value(allocator.mechanism())
, d_hashValue(-1)
, d_hashSize(0)
{
    d_value.assign<int>(value);
}

inline
Attribute::Attribute(const char            *name,
                     long                   value,
                     const allocator_type&  allocator)
: d_name(name)
, d_value(allocator.mechanism())
, d_hashValue(-1)
, d_hashSize(0)
{
    d_value.assign<long>(value);
}

inline
Attribute::Attribute(const char            *name,
                     long long              value,
                     const allocator_type&  allocator)
: d_name(name)
, d_value(allocator.mechanism())
, d_hashValue(-1)
, d_hashSize(0)
{
    d_value.assign<long long>(value);
}

inline
Attribute::Attribute(const char            *name,
                     unsigned int           value,
                     const allocator_type&  allocator)
: d_name(name)
, d_value(allocator.mechanism())
, d_hashValue(-1)
, d_hashSize(0)
{
    d_value.assign<unsigned int>(value);
}

inline
Attribute::Attribute(const char            *name,
                     unsigned long          value,
                     const allocator_type&  allocator)
: d_name(name)
, d_value(allocator.mechanism())
, d_hashValue(-1)
, d_hashSize(0)
{
    d_value.assign<unsigned long>(value);
}

inline
Attribute::Attribute(const char            *name,
                     unsigned long long     value,
                     const allocator_type&  allocator)
: d_name(name)
, d_value(allocator.mechanism())
, d_hashValue(-1)
, d_hashSize(0)
{
    d_value.assign<unsigned long long>(value);
}

inline
Attribute::Attribute(const char            *name,
                     const void            *value,
                     const allocator_type&  allocator)
: d_name(name)
, d_value(allocator.mechanism())
, d_hashValue(-1)
, d_hashSize(0)
{
    d_value.assign<const void *>(value);
}

inline
Attribute::Attribute(const char            *name,
                     const Value&           value,
                     const allocator_type&  allocator)
: d_name(name)
, d_value(value, allocator.mechanism())
, d_hashValue(-1)
, d_hashSize(0)
{
}

inline
Attribute::Attribute(const Attribute&      original,
                     const allocator_type& allocator)
: d_name(original.d_name)
, d_value(original.d_value, allocator.mechanism())
, d_hashValue(original.d_hashValue)
, d_hashSize(original.d_hashSize)
{
}

// MANIPULATORS
inline
Attribute& Attribute::operator=(const Attribute& rhs)
{
    d_name      = rhs.d_name;
    d_value     = rhs.d_value;
    d_hashValue = rhs.d_hashValue;
    d_hashSize  = rhs.d_hashSize;
    return *this;
}

inline
void Attribute::setName(const char *name)
{
    d_name = name;
    d_hashValue = -1;
}

inline
void Attribute::setValue(const Value& value)
{
    d_value = value;
    d_hashValue = -1;
}

inline
void Attribute::setValue(int value)
{
    d_value.assign(value);
    d_hashValue = -1;
}

inline
void Attribute::setValue(long value)
{
    d_value.assign(value);
    d_hashValue = -1;
}

inline
void Attribute::setValue(long long value)
{
    d_value.assign(value);
    d_hashValue = -1;
}

inline
void Attribute::setValue(unsigned int value)
{
    d_value.assign(value);
    d_hashValue = -1;
}

inline
void Attribute::setValue(unsigned long value)
{
    d_value.assign(value);
    d_hashValue = -1;
}

inline
void Attribute::setValue(unsigned long long value)
{
    d_value.assign(value);
    d_hashValue = -1;
}

inline
void Attribute::setValue(const bsl::string_view& value)
{
    d_value.assign(bsl::string(value));
    d_hashValue = -1;
}

inline
void Attribute::setValue(const char *value)
{
    d_value.assign<bsl::string>(value);
    d_hashValue = -1;
}

inline
void Attribute::setValue(const void *value)
{
    d_value.assign<const void *>(value);
    d_hashValue = -1;
}

// ACCESSORS
inline
const char *Attribute::name() const
{
    return d_name;
}

inline
const Attribute::Value& Attribute::value() const
{
    return d_value;
}

                                  // Aspects

inline
Attribute::allocator_type Attribute::get_allocator() const
{
    // Until bdlb::variant is converted to new allocator model.
    return allocator_type(d_value.getAllocator());
}

}  // close package namespace

// FREE OPERATORS
inline
bool ball::operator==(const Attribute& lhs, const Attribute& rhs)
{
    return 0 == bsl::strcmp(lhs.d_name, rhs.d_name)
        && lhs.d_value == rhs.d_value;
}

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

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