// ball_userfields.h                                                  -*-C++-*-
#ifndef INCLUDED_BALL_USERFIELDS
#define INCLUDED_BALL_USERFIELDS

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

//@PURPOSE: Provide a container of user supplied field values.
//
//@CLASSES:
//  ball::UserFields: a container of user supplied field values
//
//@SEE_ALSO: ball_userfieldvalue
//
//@DESCRIPTION: This component provides a value-semantic container-type,
// 'ball::UserFields', that represents a (randomly accessible) sequence of
// 'ball::UserFieldValue' objects.  Each user field value contained in the
// sequence functions as a discriminated union of the types described by
// 'ball::UserFieldType::Enum' (integer, double, string, etc).  Values can be
// added to the sequence using the 'append*' manipulators, and can be
// manipulated and accessed using 'operator[]'.  Additionally,
// 'ball::UserFields' exposes a random-access iterator providing non-modifiable
// access to the sequence through the 'begin' and 'end' methods.
//
///Usage
///-----
// This section illustrates intended use of this component.
//
///Example 1: Basic Use of 'ball::UserFields'
/// - - - - - - - - - - - - - - - - - - - - -
// In the following example we demonstrate populating a 'ball::UserFields'
// object with a sequence of field values.
//
// First, we define the signature for a callback, 'populateUserFields'.  Most
// often 'ball::UserFields' objects are populated by a callback, such as the
// one described by the 'ball::LoggerManagerConfiguration'
// 'UserFieldsPopulatorCallback'.
//..
//  void populateLoggingFields(ball::UserFields *fields)
//      // Populate the specified 'fields' with the username and current task
//      // identifier.  The behavior is undefined unless 'fields' is empty.
//  {
//..
// Next, we assert the precondition that 'fields' is empty:
//..
//    BSLS_ASSERT(0 == fields->length());
//..
// Now, we populate the 'fields' object with the username and current task
// identifier (for the purpose of illustration, these are simply constants):
//..
//    static const char               *TEST_USER = "testUser";
//    static const bsls::Types::Int64  TEST_TASK = 4315;
//
//    fields->appendString(TEST_USER);
//    fields->appendInt64(TEST_TASK);
//..
// Finally, for the purposes of illustration, we verify that 'fields' has been
// set correctly:
//..
//    assert(2              == fields->length());
//    assert(Type::e_STRING == fields->value(0).type());
//    assert(TEST_USER      == fields->value(0).theString());
//    assert(Type::e_INT64  == fields->value(1).type());
//    assert(TEST_TASK      == fields->value(1).theInt64());
//  }
//..

#include <balscm_version.h>

#include <ball_userfieldvalue.h>

#include <bslalg_swaputil.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_vector.h>

namespace BloombergLP {
namespace ball {

                        // ================
                        // class UserFields
                        // ================

class UserFields {
    // This class implements a value-semantic type for representing a sequence
    // of (randomly accessible) user field values.

  private:
    // DATA
    bsl::vector<ball::UserFieldValue> d_values;  // sequence of values

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

  public:
    // TYPES
    typedef bsl::vector<ball::UserFieldValue>::const_iterator ConstIterator;

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

    // CREATORS
    explicit UserFields(bslma::Allocator *basicAllocator = 0);
        // Create an empty 'UserFields' object.  Optionally specify a
        // 'basicAllocator' used to supply memory.  If 'basicAllocator' is 0,
        // the currently installed default allocator is used.

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

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

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

    void removeAll();
        // Remove all of the user field values from this object.  After this
        // method is called 'length' is 0.

    void append(const UserFieldValue& value);
        // Append the specified 'value' to this object.

    void appendNull();
        // Append an element having the unset value to this object.

    void appendInt64(bsls::Types::Int64 value);
    void appendDouble(double value);
    void appendString(const bsl::string_view& value);
    void appendDatetimeTz(const bdlt::DatetimeTz& value);
    void appendCharArray(const bsl::vector<char>& value);
        // Append the specified 'value' to this object.

    ball::UserFieldValue& operator[](int index);
    ball::UserFieldValue& value(int index);
        // Return a reference providing modifiable access to the value at the
        // specified 'index'.  The behavior is undefined unless
        // '0 <= index && index < length()'.

                                  // Aspects

    void swap(UserFields& 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.  The behavior is undefined unless this
        // object was created with the same allocator as 'other'.

    // ACCESSORS
    ConstIterator begin() const;
        // Return an iterator providing non-modifiable access to the first
        // element in the sequence of user field values maintained by this
        // object, or the 'end' iterator if this object is empty.

    ConstIterator end() const;
        // Return an iterator providing non-modifiable access to the
        // past-the-end element in the sequence of user field values
        // maintained by this object.

    int length () const;
        // Return the number of user field values in this object.

    const ball::UserFieldValue& operator[](int index) const;
    const ball::UserFieldValue& value(int index) const;
        // Return a reference providing non-modifiable access to the value at
        // the specified 'index'.  The behavior is undefined unless
        // '0 <= index && index < length()'.

                                  // 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 UserFields& lhs, const UserFields& rhs);
    // Return 'true' if the specified 'lhs' and 'rhs' objects have the same
    // value, and 'false' otherwise.  Two 'ball::UserFields' objects have the
    // same value if they have the same number of elements, and each element in
    // 'lhs' has the same value as corresponding element at the same index in
    // 'rhs'.

bool operator!=(const UserFields& lhs, const UserFields& rhs);
    // Return 'true' if the specified 'lhs' and 'rhs' objects do not have the
    // same value, and 'false' otherwise.  Two 'UserFields' objects do not
    // have the same value if they have a different number of elements, or if
    // any element in 'lhs' has a different value from the corresponding
    // element at the same index in 'rhs'.

bsl::ostream& operator<<(bsl::ostream& stream, const UserFields& 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::UserFields& a, ball::UserFields& b);
    // Swap the value of the specified 'a' object with the value of the
    // specified 'b' object.  If 'a' and 'b' were created with the same
    // allocator, then this method provides the no-throw exception-safety
    // guarantee; otherwise, it provides the basic guarantee.

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

                        // ----------------
                        // class UserFields
                        // ----------------

// CREATORS
inline
UserFields::UserFields(bslma::Allocator *basicAllocator)
: d_values(basicAllocator)
{
}

inline
UserFields::UserFields(const UserFields&  original,
                       bslma::Allocator  *basicAllocator)
: d_values(original.d_values, basicAllocator)
{
}

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

inline
void UserFields::removeAll()
{
    d_values.clear();
}

inline
void UserFields::appendNull()
{
    d_values.emplace_back();
}

inline
void UserFields::appendInt64(bsls::Types::Int64 value)
{
    d_values.emplace_back(value);
}

inline
void UserFields::appendDouble(double value)
{
    d_values.emplace_back(value);
}

inline
void UserFields::appendString(const bsl::string_view& value)
{
    d_values.emplace_back(value);
}

inline
void UserFields::appendDatetimeTz(const bdlt::DatetimeTz& value)
{
    d_values.emplace_back(value);
}

inline
void UserFields::appendCharArray(const bsl::vector<char>& value)
{
    d_values.emplace_back(value);
}

inline
UserFieldValue& UserFields::operator[](int index)
{
    return d_values[index];
}

inline
UserFieldValue& UserFields::value(int index)
{
    return d_values[index];
}

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

    d_values.swap(other.d_values);
}

// ACCESSORS
inline
bslma::Allocator *UserFields::allocator() const
{
    return d_values.get_allocator().mechanism();
}

inline
UserFields::ConstIterator UserFields::begin() const
{
    return d_values.begin();
}

inline
UserFields::ConstIterator UserFields::end() const
{
    return d_values.end();
}

inline
int UserFields::length() const
{
    return static_cast<int>(d_values.size());
}

inline
const UserFieldValue& UserFields::operator[](int index) const
{
    return d_values[index];
}

inline
const UserFieldValue& UserFields::value(int index) const
{
    return d_values[index];
}

}  // close package namespace

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

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

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

// FREE FUNCTIONS
inline
void ball::swap(UserFields& a, UserFields& b)
{
    bslalg::SwapUtil::swap(&a.d_values, &b.d_values);
}

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