// ball_userfieldvalue.h                                              -*-C++-*-
#ifndef INCLUDED_BALL_USERFIELDVALUE
#define INCLUDED_BALL_USERFIELDVALUE

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

//@PURPOSE: Provide a type for the value of a user supplied field.
//
//@CLASSES:
//  ball::UserFieldValue: the value of a user supplied field
//
//@SEE_ALSO: ball_userfields, ball_userfieldtype
//
//@DESCRIPTION: This component provides a value-semantic class,
// 'ball::UserFieldValue', that represents the value of a user supplied log
// field value.  A user field value acts as a discriminated union, and may
// represent a value of any of types described in 'ball::UserFieldType' or an
// unset value (indicated by the type 'ball::UserFieldType::e_VOID').
//
///Usage
///-----
// This section illustrates intended use of this component.
//
///Example 1: Basic Use of 'ball::UserFieldValue'
/// - - - - - - - - - - - - - - - - - - - - - - -
// The following snippets of code illustrate how to create and use a
// 'ball::UserFieldValue' object.  Note that 'ball::UserFieldValue' objects are
// typically used in a description of a sequence of user fields (see
// 'ball_userfields').
//
// First, we create a default 'ball::UserFieldValue', 'valueA', and observe
// that it is in the unset state, meaning that 'isUnset' is true and its type
// is 'ball::UserFieldValue::e_VOID':
//..
//  ball::UserFieldValue valueA;
//
//  assert(true                         == valueA.isUnset());
//  assert(ball::UserFieldValue::e_VOID == valueA.type());
//..
// Next, we create a second 'ball::UserFieldValue' having the value 5, and then
// confirm its value and observe that it does not compare equal to the
// 'valueA':
//..
//  ball::UserFieldValue valueB(5);
//
//  assert(false                         == valueB.isUnset());
//  assert(ball::UserFieldValue::e_INT64 == valueB.type());
//  assert(5                             == valueB.theInt64();
//
//  assert(valueA != valueB);
//..
// Finally, we call 'reset' of 'valueB' resetting it to the unset state, and
// observe that 'valueA' now compares equal to 'valueB':
//..
//  valueB.reset();
//
//  assert(valueA == valueB);
//..

#include <balscm_version.h>

#include <ball_userfieldtype.h>

#include <bdlb_variant.h>

#include <bdlt_datetimetz.h>

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

#include <bslmf_nestedtraitdeclaration.h>

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

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

namespace BloombergLP {
namespace ball {

                        // ====================
                        // class UserFieldValue
                        // ====================

class UserFieldValue {
    // This class implements a value-semantic type for representing the value
    // of a user field in a log record.  A user field value acts as a
    // discriminated union, and may represent a value of any of the types
    // described in 'ball::UserFieldType' or an unset value (indicated by type
    // 'ball::UserFieldType::e_VOID').

    // PRIVATE TYPES
    typedef bdlb::Variant<bsls::Types::Int64,
                          double,
                          bsl::string,
                          bdlt::DatetimeTz,
                          bsl::vector<char> > ValueVariant;

    // DATA
    ValueVariant d_value;  // value

    // FRIENDS
    friend bool operator==(const UserFieldValue&, const UserFieldValue&);
    friend void swap(UserFieldValue&, UserFieldValue&);

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

    // CREATORS
    explicit UserFieldValue(bslma::Allocator *basicAllocator = 0);
        // Create a user field value having the unset value.  Optionally
        // specify a 'basicAllocator' used to supply memory.  If
        // 'basicAllocator' is 0, the currently installed default allocator is
        // used.

    explicit UserFieldValue(bsls::Types::Int64        value,
                            bslma::Allocator         *basicAllocator = 0);
    explicit UserFieldValue(double                    value,
                            bslma::Allocator         *basicAllocator = 0);
    explicit UserFieldValue(const bsl::string_view&   value,
                            bslma::Allocator         *basicAllocator = 0);
    explicit UserFieldValue(const bdlt::DatetimeTz&   value,
                            bslma::Allocator         *basicAllocator = 0);
    explicit UserFieldValue(const bsl::vector<char>&  value,
                            bslma::Allocator         *basicAllocator = 0);
        // Create a user field value having the specified 'value'.  Optionally
        // specify a 'basicAllocator' used to supply memory.  If
        // 'basicAllocator' is 0, the currently installed default allocator is
        // used.

    template <class t_INTEGRAL_TYPE>
    explicit UserFieldValue(
        t_INTEGRAL_TYPE   value,
        bslma::Allocator *basicAllocator = 0,
        typename bsl::enable_if<bsl::is_integral<t_INTEGRAL_TYPE>::value>::type
            * = 0)
    : d_value(static_cast<bsls::Types::Int64>(value), basicAllocator) {}
        // Create a user field value having the specified integral 'value'.
        // Optionally specify a 'basicAllocator' used to supply memory.  If
        // 'basicAllocator' is 0, the currently installed default allocator is
        // used.
        //
        // Note that this constructor is provided to disambiguate between
        // constructors taking 'double' and 'bsls::Types::Int64' when supplied
        // an integer that is not of type 'bsls::Types::Int64'.  Also note that
        // the implementation is (temporarily) provided inline to avoid issues
        // with MSVC 2008.

    UserFieldValue(const UserFieldValue&  original,
                   bslma::Allocator      *basicAllocator = 0);
        // Create a 'UserFieldValue' object having the same value as the
        // specified 'original' object.  Optionally specify a 'basicAllocator'
        // used to supply memory.  If 'basicAllocator' is 0, the currently
        // installed default allocator is used.

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

    // MANIPULATORS
    UserFieldValue& operator=(const UserFieldValue& rhs);
        // Assign to this object the value of the specified 'rhs' object, and
        // return a reference providing modifiable access to this object.

    void reset();
        // Set this object to have the unset value.  After this operation,
        // 'type() == ball::UserFieldType::e_VOID'.

    void setInt64(bsls::Types::Int64 value);
        // Set this object to have the specified 'value'.  After this
        // operation, 'type() == ball::UserFieldType::e_INT64'.

    void setDouble(double value);
        // Set this object to have the specified 'value'.  After this
        // operation, 'type() == ball::UserFieldType::e_DOUBLE'.

    void setString(const bsl::string_view& value);
        // Set this object to have the specified 'value'.  After this
        // operation, 'type() == ball::UserFieldType::e_STRING'.

    void setDatetimeTz(const bdlt::DatetimeTz& value);
        // Set this object to have the specified 'value'.  After this
        // operation, 'type() == ball::UserFieldType::e_DATETIMETZ'.

    void setCharArray(const bsl::vector<char>& value);
        // Set this object to have the specified 'value'.  After this
        // operation, 'type() == ball::UserFieldType::e_CHAR_ARRAY'.

                                  // Aspects

    void swap(UserFieldValue& other);
        // Efficiently exchange the value of this object with the value of the
        // specified 'other' object.  This method provides the no-throw
        // exception-safety guarantee if either 'type()' is the same as
        // 'other.type()', or neither 'type()' nor 'other.type()' is a type
        // that requires allocation; otherwise, it provides the basic
        // guarantee.  The behavior is undefined unless this object was created
        // with the same allocator as 'other'.

    // ACCESSORS
    bool isUnset() const;
        // Return 'true' if this object has the unset value, and 'false'
        // otherwise.  Note that if 'isUnset()' returns 'true', then 'type()'
        // returns 'ball::UserFieldType::e_VOID'.

    ball::UserFieldType::Enum type() const;
        // Return the type of this user field value.  The type
        // 'ball::UserFieldValue::e_VOID' represents the unset value.

    const bsls::Types::Int64& theInt64() const;
        // Return a reference providing non-modifiable access to the 64-bit
        // integer value of this object.  The behavior is undefined unless
        // 'type() == ball::UserFieldType::e_INT64'.

    const double& theDouble() const;
        // Return a reference providing non-modifiable access to the double
        // value of this object.  The behavior is undefined unless
        // 'type() == ball::UserFieldType::e_DOUBLE'.

    const bsl::string& theString() const;
        // Return a reference providing non-modifiable access to the string
        // value of this object.  The behavior is undefined unless
        // 'type() == ball::UserFieldType::e_STRING'.

    const bdlt::DatetimeTz& theDatetimeTz() const;
        // Return a reference providing non-modifiable access to the
        // 'DatetimeTz' value of this object.  The behavior is undefined
        // unless 'type() == ball::UserFieldType::e_DATETIMETZ'.

    const bsl::vector<char>& theCharArray() const;
        // Return a reference providing non-modifiable access to the
        // 'bsl::vector<char>' value of this object.  The behavior is undefined
        // unless 'type() == ball::UserFieldType::e_CHAR_ARRAY'.

                                  // Aspects

    bslma::Allocator *allocator() const;
        // Return the allocator used by this object to supply memory.  Note
        // that if no allocator was supplied at construction the currently
        // installed default allocator is used.

    bsl::ostream& print(bsl::ostream& stream,
                        int           level = 0,
                        int           spacesPerLevel = 4) const;
        // Write the value of this object to the specified output 'stream' in
        // a human-readable format, and return a reference to 'stream'.
        // Optionally specify an initial indentation 'level', whose absolute
        // value is incremented recursively for nested objects.  If 'level' is
        // specified, optionally specify 'spacesPerLevel', whose absolute
        // value indicates 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.  Note that the format
        // is not fully specified, and can change without notice.
};

// FREE OPERATORS
bool operator==(const UserFieldValue& lhs, const UserFieldValue& rhs);
    // Return 'true' if the specified 'lhs' and 'rhs' objects have the same
    // value, and 'false' otherwise.  Two 'UserFieldValue' objects have the
    // same value if they have the same type, and (if the type is not
    // 'e_VOID') the value of that type (as accessed through 'the*' methods)
    // is the same.

bool operator!=(const UserFieldValue& lhs, const UserFieldValue& rhs);
    // Return 'true' if the specified 'lhs' and 'rhs' objects do not have the
    // same value, and 'false' otherwise.  Two 'UserFieldValue' objects do not
    // have the same value if their type is not the same, or (if their type
    // is not 'e_VOID') the value of that type (as accessed through 'the*'
    // methods) is not the same.

bsl::ostream& operator<<(bsl::ostream& stream, const UserFieldValue& object);
    // Write the value of the specified 'object' to the specified output
    // 'stream' in a single-line format, and return a reference to 'stream'.
    // If 'stream' is not valid on entry, this operation has no effect.  Note
    // that this human-readable format is not fully specified, can change
    // without notice, and is logically equivalent to:
    //..
    //  print(stream, 0, -1);
    //..

// FREE FUNCTIONS
void swap(ball::UserFieldValue& a, ball::UserFieldValue& b);
    // Swap the value of the specified 'a' object with the value of the
    // specified 'b' object.  This method provides the no-throw
    // exception-safety guarantee if either 'a.type()' is the same as
    // 'b.type()' and 'a' and 'b' were created with the same allocator, or
    // neither 'a.type()' nor 'b.type()' is a type that requires allocation;
    // otherwise, it provides the basic guarantee.

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

                        // --------------------
                        // class UserFieldValue
                        // --------------------

// CREATORS
inline
UserFieldValue::UserFieldValue(bslma::Allocator *basicAllocator)
: d_value(basicAllocator)
{
}

inline
UserFieldValue::UserFieldValue(bsls::Types::Int64  value,
                               bslma::Allocator   *basicAllocator)
: d_value(value, basicAllocator)
{
}

inline
UserFieldValue::UserFieldValue(double value, bslma::Allocator *basicAllocator)
: d_value(value, basicAllocator)
{
}

inline
UserFieldValue::UserFieldValue(const bsl::string_view&  value,
                               bslma::Allocator        *basicAllocator)
: d_value(basicAllocator)
{
    d_value.assignTo<bsl::string>(value);
}

inline
UserFieldValue::UserFieldValue(const bdlt::DatetimeTz&  value,
                               bslma::Allocator        *basicAllocator)
: d_value(value, basicAllocator)
{
}

inline
UserFieldValue::UserFieldValue(const bsl::vector<char>&  value,
                               bslma::Allocator         *basicAllocator)
: d_value(value, basicAllocator)
{
}

inline
UserFieldValue::UserFieldValue(const UserFieldValue&  original,
                               bslma::Allocator      *basicAllocator)
: d_value(original.d_value, basicAllocator)
{
}

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

inline
void UserFieldValue::reset()
{
    d_value.reset();
}

inline
void UserFieldValue::setInt64(bsls::Types::Int64 value)
{
    d_value.assign(value);
}

inline
void UserFieldValue::setDouble(double value)
{
    d_value.assign(value);
}

inline
void UserFieldValue::setString(const bsl::string_view& value)
{
    d_value.assignTo<bsl::string>(value);
}

inline
void UserFieldValue::setDatetimeTz(const bdlt::DatetimeTz& value)
{
    d_value.assign(value);
}

inline
void UserFieldValue::setCharArray(const bsl::vector<char>& value)
{
    d_value.assign(value);
}

inline
void UserFieldValue::swap(UserFieldValue& other)
{
    BSLS_ASSERT(allocator() == other.allocator());

    d_value.swap(other.d_value);
}

// ACCESSORS
inline
bool UserFieldValue::isUnset() const
{
    return d_value.isUnset();
}

inline
const bsls::Types::Int64& UserFieldValue::theInt64() const
{
    BSLS_ASSERT_SAFE(d_value.is<bsls::Types::Int64>());

    return d_value.the<bsls::Types::Int64>();
}

inline
const double& UserFieldValue::theDouble() const
{
    BSLS_ASSERT_SAFE(d_value.is<double>());

    return d_value.the<double>();
}

inline
const bsl::string& UserFieldValue::theString() const
{
    BSLS_ASSERT_SAFE(d_value.is<bsl::string>());

    return d_value.the<bsl::string>();
}

inline
const bdlt::DatetimeTz& UserFieldValue::theDatetimeTz() const
{
    BSLS_ASSERT_SAFE(d_value.is<bdlt::DatetimeTz>());

    return d_value.the<bdlt::DatetimeTz>();
}

inline
const bsl::vector<char>& UserFieldValue::theCharArray() const
{
    BSLS_ASSERT_SAFE(d_value.is<bsl::vector<char> >());

    return d_value.the<bsl::vector<char> >();
}

                                  // Aspects

inline
bslma::Allocator *UserFieldValue::allocator() const
{
    return d_value.getAllocator();
}

}  // close package namespace

// FREE OPERATORS
inline
bool ball::operator==(const UserFieldValue& lhs, const UserFieldValue& rhs)
{
    return lhs.d_value == rhs.d_value;
}

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

inline
bsl::ostream& ball::operator<<(bsl::ostream&         stream,
                               const UserFieldValue& object)
{
    return object.print(stream, 0, -1);
}

// FREE FUNCTIONS
inline
void ball::swap(UserFieldValue& a, UserFieldValue& b)
{
    // 'bdlb::Variant' member 'swap' supports differing allocators.

    a.d_value.swap(b.d_value);
}

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