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

/// 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`).
class UserFieldValue {

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

    /// 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(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.
    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 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.
    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 `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(const UserFieldValue&  original,
                   bslma::Allocator      *basicAllocator = 0);

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

    // MANIPULATORS

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

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

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

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

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

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

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

                                  // Aspects

    /// 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`.
    void swap(UserFieldValue& other);

    // ACCESSORS

    /// 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`.
    bool isUnset() const;

    /// Return the type of this user field value.  The type
    /// `ball::UserFieldValue::e_VOID` represents the unset value.
    ball::UserFieldType::Enum type() 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 bsls::Types::Int64& theInt64() 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 double& theDouble() 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 bsl::string& theString() 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 bdlt::DatetimeTz& theDatetimeTz() 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`.
    const bsl::vector<char>& theCharArray() const;

                                  // Aspects

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

// FREE OPERATORS

/// 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.
bool operator!=(const UserFieldValue& lhs, const UserFieldValue& rhs);

/// 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);
/// ```
bsl::ostream& operator<<(bsl::ostream& stream, const UserFieldValue& object);

// FREE FUNCTIONS

/// 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.
void swap(ball::UserFieldValue& a, ball::UserFieldValue& b);

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