// bdld_datumintmapbuilder.h                                          -*-C++-*-
#ifndef INCLUDED_BDLD_DATUMINTMAPBUILDER
#define INCLUDED_BDLD_DATUMINTMAPBUILDER

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

//@PURPOSE: Provide a utility to build a `Datum` object holding an int-map.
//
//@CLASSES:
//  bdld::DatumIntMapBuilder: utility to build `Datum` objects holding int-maps
//
//@SEE_ALSO: bdld_datum
//
//@DESCRIPTION: This component defines a mechanism, `bdld::DatumIntMapBuilder`,
// used to populate a `Datum` int-map value in an exception-safe manner.  In
// addition to providing exception safety, a `DatumIntMapBuilder` is
// particularly useful when the size of the int-map to be constructed is not
// known in advance.  The user can append elements to the datum int-map as
// needed, and when there are no more elements to append the user calls
// `commit` or `sortAndCommit` and ownership of the populated `Datum` object is
// transferred to the caller.  After calling `commit` or `sortAndCommit`, no
// additional elements can be appended to the `Datum` int-map value.  Note that
// `sortAndCommit` method will sort the populated int-map (by keys) and tag the
// resulting `Datum` int-map value as sorted.  Also note that the user can
// insert elements in a (ascending) sorted order and tag the int-map as sorted.
// The behavior is undefined if unsorted int-map is tagged sorted.
//
///Usage
///-----
// This section illustrates intended use of this component.
//
///Example 1: Basic Syntax
///- - - - - - - - - - - -
// Suppose we need a data map for some UX data.  The keys of the map are 32-bit
// integers; and the values in that map can be different types.  The following
// code illustrates how to use `bdld::DatumIntMapBuilder` to create such map
// easily.
//
// First, we need data to fill our int-map:
// ```
// bslma::TestAllocator ta("test", veryVeryVerbose);
//
// DatumIntMapEntry formData[] = {
//     DatumIntMapEntry(1, Datum::createStringRef("Bart", &ta)),
//     DatumIntMapEntry(2, Datum::createStringRef("Simpson", &ta)),
//     DatumIntMapEntry(3, Datum::createStringRef("male", &ta)),
//     DatumIntMapEntry(4, Datum::createInteger(10))
// };
//
// const size_t DATA_SIZE  = sizeof(formData) / sizeof(DatumIntMapEntry);
// ```
// Next, we create an object of `DatumIntMapBuilder` class with initial
// capacity sufficient for storing all our data:
// ```
// DatumIntMapBuilder builder(DATA_SIZE, &ta);
// ```
// Then, we load our builder with these data:
// ```
// for (size_t i = 0; i < DATA_SIZE; ++i) {
//     builder.pushBack(formData[i].key(), formData[i].value());
// }
// ```
// Next, we adopt the int-map, held by our builder, by newly created `Datum`
// object:
// ```
// Datum form = builder.commit();
// ```
// Now, we can check that all data have been correctly added to the int-map at
// the required order:
// ```
// assert(true == form.isIntMap());
// assert(DATA_SIZE == form.theIntMap().size());
//
// assert(1           == form.theIntMap()[0].key());
// assert(true        == form.theIntMap()[0].value().isString());
// assert("Bart"      == form.theIntMap()[0].value().theString());
//
// assert(2           == form.theIntMap()[1].key());
// assert(true        == form.theIntMap()[1].value().isString());
// assert("Simpson"   == form.theIntMap()[1].value().theString());
//
// assert(3           == form.theIntMap()[2].key());
// assert(true        == form.theIntMap()[2].value().isString());
// assert("male"      == form.theIntMap()[2].value().theString());
//
// assert(4           == form.theIntMap()[3].key());
// assert(true        == form.theIntMap()[3].value().isInteger());
// assert(10          == form.theIntMap()[3].value().theInteger());
// ```
// Finally, we destroy the `Datum` object to release all allocated memory
// correctly:
// ```
// Datum::destroy(form, &ta);
// assert(0 == ta.numBytesInUse());
// ```

#include <bdlscm_version.h>

#include <bdld_datum.h>

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

#include <bslmf_nestedtraitdeclaration.h>

#include <bsls_types.h>

namespace BloombergLP {
namespace bdld {

                          // ========================
                          // class DatumIntMapBuilder
                          // ========================

/// This `class` provides a mechanism to build a `Datum` object having an
/// int-map value in an exception-safe manner.  See `bdld_datum` for more
/// information about integer maps.
class DatumIntMapBuilder {

  public:
    // TYPES

    /// `SizeType` is an alias for a unsigned integral value, representing
    /// the capacity or size of a datum int-map.
    typedef bsls::Types::size_type SizeType;

    typedef bsl::allocator<char> allocator_type;

  private:
    // DATA
    DatumMutableIntMapRef d_mapping;   // mutable access to the datum int-map

    SizeType              d_capacity;  // capacity of the datum int-map

    bool                  d_sorted;    // underlying int-map is sorted

    allocator_type        d_allocator; // allocator for memory

  private:
    // NOT IMPLEMENTED
    DatumIntMapBuilder(const DatumIntMapBuilder&);
    DatumIntMapBuilder& operator=(const DatumIntMapBuilder&);

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

    // CREATORS

    /// Create a `DatumIntMapBuilder` object that will administer the
    /// process of building a `Datum` int-map.  Optionally specify an
    /// `initialCapacity` for the int-map.  If `initialCapacity` is not
    /// supplied, the initial capacity of the int-map is 0.  Optionally
    /// specify an `allocator` (e.g., the address of a `bslma::Allocator`
    /// object) to supply memory; otherwise, the default allocator is used.
    DatumIntMapBuilder();
    explicit DatumIntMapBuilder(const allocator_type& allocator);
    explicit DatumIntMapBuilder(
                           SizeType              initialCapacity,
                           const allocator_type& allocator = allocator_type());

    /// Destroy this object.  If this object is holding a datum int-map that
    /// has not been adopted, then the datum int-map is disposed after
    /// destroying each of its elements.
    ~DatumIntMapBuilder();

    // MANIPULATORS

    /// Append the specified array `entries` having the specified `size` to
    /// the `Datum` int-map being build by this object.  The behavior is
    /// undefined unless `0 != entries && 0 != size` and each element in
    /// `entries` that needs dynamic memory is allocated with the same
    /// allocator that was used to construct this object.  The behavior is
    /// undefined if `commit` or `sortAndCommit` has already been called on
    /// this object.
    void append(const DatumIntMapEntry *entries, SizeType size);

    /// Return a `Datum` int-map value holding the elements supplied to
    /// `pushBack` or `append`.  The caller is responsible for releasing the
    /// resources of the returned `Datum` object.  Calling this method
    /// indicates that the caller is finished building the `Datum` int-map
    /// and no further values shall be appended.  The behavior is undefined
    /// if any method of this object, other than its destructor, is called
    /// after `commit` invocation.
    Datum commit();

    /// Append the entry with the specified `key` and the specified `value`
    /// to the `Datum` int-map being build by this object.  The behavior is
    /// undefined if `value` needs dynamic memory and was allocated using a
    /// different allocator than the one used to construct this object.  The
    /// behavior is also undefined if `commit` or `sortAndCommit` has
    /// already been called on this object.
    void pushBack(int key, const Datum& value);

    /// Mark the Datum int-map being built by this object as sorted if the
    /// specified `value` is `true` and mark it unsorted otherwise.  This
    /// function does not sort the int-map entries, or mark them to be
    /// sorted later; the function should be used to indicate if the entries
    /// are being appended in sorted order.  The behavior is undefined if
    /// `commit` or `sortAndCommit` has already been called on this object.
    /// The behavior is also undefined if the int-map being constructed is
    /// marked sorted, but the entries are not appended in sorted order.
    /// Note also that the int-map being constructed is marked unsorted by
    /// default.
    void setSorted(bool value);

    /// Return a `Datum` int-map value holding the elements supplied to
    /// `pushBack` or `append` sorted by their keys.  The caller is
    /// responsible for releasing the resources of the returned `Datum`
    /// object.  Calling this method indicates that the caller is finished
    /// building the `Datum` int-map and no further values shall be
    /// appended.  The behavior is undefined if any method of this object,
    /// other than its destructor, is called after `sortAndCommit`
    /// invocation.
    Datum sortAndCommit();

    // ACCESSORS

    /// Return the capacity of the held `Datum` int-map.  The behavior is
    /// undefined if `commit` or `sortAndCommit` has already been called on
    /// this object.  Note that similar to the capacity of a `vector`, the
    /// returned capacity has no bearing on the value of the `Datum` being
    /// constructed, but does indicate at which point additional memory will
    /// be required to grow the `Datum` int-map being built.
    SizeType capacity() 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.
    allocator_type get_allocator() const;

    /// Return the size of the held `Datum` int-map.  The behavior is
    /// undefined if `commit` or `sortAndCommit` has already been called on
    /// this object.
    SizeType size() const;
};

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

                          // ------------------------
                          // class DatumIntMapBuilder
                          // ------------------------

// ACCESSORS
inline
DatumIntMapBuilder::SizeType DatumIntMapBuilder::capacity() const
{
    return d_capacity;
}

inline
DatumIntMapBuilder::allocator_type DatumIntMapBuilder::get_allocator() const
{
    return d_allocator;
}

inline
DatumIntMapBuilder::SizeType DatumIntMapBuilder::size() const
{
    if (d_capacity) {
        return *d_mapping.size();                                     // RETURN
    }
    return 0;
}

}  // close package namespace
}  // close enterprise namespace

#endif

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