// bdld_datummapbuilder.h                                             -*-C++-*-
#ifndef INCLUDED_BDLD_DATUMMAPBUILDER
#define INCLUDED_BDLD_DATUMMAPBUILDER

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

//@PURPOSE: Provide a utility to build a 'Datum' object holding a map.
//
//@CLASSES:
//  bdld::DatumMapBuilder: utility to build a 'Datum' object holding a map
//
//@SEE_ALSO: bdld_datum, bdld_datummapowningkeysbuilder
//
//@DESCRIPTION: This component defines a mechanism, 'bdld::DatumMapBuilder',
// used to populate a 'Datum' map value in an exception-safe manner.  In
// addition to providing exception safety, a 'DatumMapBuilder' is particularly
// useful when the size of the map to be constructed is not known in advance.
// The user can append elements to the datum 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' map value.  Note that 'sortAndCommit' method will
// sort the populated map (by keys) and tag the resulting 'Datum' map value as
// sorted.  Also note that the user can insert elements in a (ascending) sorted
// order and tag the map as sorted.  The behaviour is undefined if unsorted map
// is tagged sorted.
//
// The only difference between this component and
// 'bdld_datummapowningkeysbuilder' is that this component does not make a copy
// of the map entries keys and the resulting 'Datum' object does not own memory
// for the map entries keys.
//
///Usage
///-----
// This section illustrates intended use of this component.
//
///Example 1: Basic Syntax
///- - - - - - - - - - - -
// Suppose we need a map for some personal data.  And the values in that map
// can be different types.   The following code illustrates how to use
// 'bdld::DatumMapBuilder' to create such map easily.
//
// First, we need data to fill our map:
//..
//  bslma::TestAllocator ta("test", veryVeryVerbose);
//
//  DatumMapEntry bartData[] = {
//      DatumMapEntry(StringRef("firstName"),
//                    Datum:: createStringRef("Bart", &ta)),
//      DatumMapEntry(StringRef("lastName"),
//                    Datum:: createStringRef("Simpson", &ta)),
//      DatumMapEntry(StringRef("gender"),
//                    Datum::createStringRef("male", &ta)),
//      DatumMapEntry(StringRef("age"), Datum::createInteger(10))
//  };
//
//  const size_t DATA_SIZE  = sizeof(bartData) / sizeof(DatumMapEntry);
//..
// Next, we create an object of 'DatumMapBuilder' class with initial capacity
// sufficient for storing all our data:
//..
//  DatumMapBuilder builder(DATA_SIZE, &ta);
//..
// Then, we load our builder with these data:
//..
//  for (size_t i = 0; i < DATA_SIZE; ++i) {
//      builder.pushBack(bartData[i].key(), bartData[i].value());
//  }
//..
// Next, we adopt the map, held by our builder, by newly created 'Datum'
// object:
//..
//  Datum bart = builder.commit();
//..
// Now, we can check that all data have been correctly added to the map at the
// required order:
//..
//  assert(true == bart.isMap());
//  assert(DATA_SIZE == bart.theMap().size());
//
//  assert("firstName" == bart.theMap()[0].key());
//  assert(true        == bart.theMap()[0].value().isString());
//  assert("Bart"      == bart.theMap()[0].value().theString());
//
//  assert("lastName"  == bart.theMap()[1].key());
//  assert(true        == bart.theMap()[1].value().isString());
//  assert("Simpson"   == bart.theMap()[1].value().theString());
//
//  assert("gender"    == bart.theMap()[2].key());
//  assert(true        == bart.theMap()[2].value().isString());
//  assert("male"      == bart.theMap()[2].value().theString());
//
//  assert("age"       == bart.theMap()[3].key());
//  assert(true        == bart.theMap()[3].value().isInteger());
//  assert(10          == bart.theMap()[3].value().theInteger());
//..
// Finally, we destroy the 'Datum' object to release all allocated memory
// correctly:
//..
//  Datum::destroy(bart, &ta);
//  assert(0 == ta.numBytesInUse());
//..

#include <bdlscm_version.h>

#include <bdld_datum.h>

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

#include <bslmf_nestedtraitdeclaration.h>

#include <bsls_types.h>

namespace BloombergLP {
namespace bdld {

                           // =====================
                           // class DatumMapBuilder
                           // =====================

class DatumMapBuilder {
    // This 'class' provides a mechanism to build a 'Datum' object having a map
    // value in an exception-safe manner.

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

    typedef bsl::allocator<char> allocator_type;

  private:
    // DATA
    DatumMutableMapRef d_mapping;    // mutable access to the datum map
    SizeType           d_capacity;   // capacity of the datum map
    bool               d_sorted;     // underlying map is sorted or not
    allocator_type     d_allocator;  // allocator for memory

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

  public:
    // TRAITS
    BSLMF_NESTED_TRAIT_DECLARATION(DatumMapBuilder, bslma::UsesBslmaAllocator);
        // 'DatumMapBuilder' is allocator-aware.

    // CREATORS
    DatumMapBuilder();
    explicit DatumMapBuilder(const allocator_type &allocator);
    explicit DatumMapBuilder(
                           SizeType              initialCapacity,
                           const allocator_type& allocator = allocator_type());
        // Create a 'DatumMapBuilder' object that will administer the process
        // of building a 'Datum' map.  Optionally specify an 'initialCapacity'
        // for the map.  If 'initialCapacity' is not supplied, the initial
        // capacity of the 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.

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

    // MANIPULATORS
    void append(const DatumMapEntry *entries, SizeType size);
        // Append the specified array 'entries' having the specified 'size' to
        // the 'Datum' 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.

    Datum commit();
        // Return a 'Datum' 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' 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.

    void pushBack(const bslstl::StringRef& key, const Datum& value);
        // Append the entry with the specified 'key' and the specified 'value'
        // to the 'Datum' 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 setSorted(bool value);
        // Mark the Datum 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 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 map being constructed is
        // marked sorted, but the entries are not appended in sorted order.
        // Note also that the map being constructed is marked unsorted by
        // default.

    Datum sortAndCommit();
        // Return a 'Datum' 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' 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.

    // ACCESSORS
    SizeType capacity() const;
        // Return the capacity of the held 'Datum' 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' map being built.

    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.

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

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

                      // ---------------------
                      // class DatumMapBuilder
                      // ---------------------

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

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

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

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

#endif

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