// bdld_datumarraybuilder.h                                           -*-C++-*-
#ifndef INCLUDED_BDLD_DATUMARRAYBUILDER
#define INCLUDED_BDLD_DATUMARRAYBUILDER

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

//@PURPOSE: Provide a utility to build a `Datum` object holding an array.
//
//@CLASSES:
//  bdld::DatumArrayBuilder: utility to build a `Datum` object holding an array
//
//@SEE_ALSO: bdld_datum
//
//@DESCRIPTION: This component defines a mechanism, `bdld::DatumArrayBuilder`,
// used to populate a `Datum` array value in an exception-safe manner.  In
// addition to providing exception safety, a `DatumArrayBuilder` is
// particularly useful when the length of the array to be constructed is not
// known in advance.  The user can append elements to the datum array as
// needed, and when there are no more elements to append the user calls
// `commit` and ownership of the populated `Datum` object is transferred to the
// caller.  After the call to `commit`, no additional elements can be appended
// to the `Datum` array value.
//
///Usage
///-----
// This section illustrates intended use of this component.
//
///Example 1: Using a `DatumArrayBuilder` to Create a `Datum` array.
///- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Suppose we receive a string that is constructed by streaming a bunch of
// values together in the format shown below:
// ```
// "2.34,4,hi there,true"
// ```
// Notice that the values are separated by a `,`.  Also note that a `,` is not
// allowed to be part of a string value to simplify the implementation of the
// utility that parses this string.  The following code snippets illustrate how
// to create a `Datum` object that holds an array of `Datum` objects
// constructed using the streamed values.
//
// First we define a function `nextValue` that we will use to tokenize the
// input string:
// ```
// /// Extract the next value from a list of comma separated values in the
// /// specified `input` string and load it in the specified `value`.
// /// Return the index of the next value within `input`.
// bsl::size_t nextValue(bsl::string *value, const bsl::string& input)
// {
//     if (input.empty()) {
//         return bsl::string::npos;
//     }
//     int start = 0;
//     bsl::size_t nextIndex = input.find(',', start);
//     if (bsl::string::npos != nextIndex) {
//         *value = input.substr(start, nextIndex - start);
//     }
//     else {
//         *value = input.substr(start);
//     }
//     return nextIndex;
// }
// ```
// Next, we define a function `convertToDatum` that will convert a string
// token into a `Datum` scalar value:
// ```
// // Convert the specified `token` into the appropriate type of scalar
// // value and then create and return a `Datum` object using that value.
// // Use the specified `basicAllocator` to supply memory.
// bdld::Datum convertToDatum(const bsl::string&  token,
//                            bslma::Allocator   *basicAllocator)
// {
//     bool isInteger = true;
//     bool isDouble = false;
//     bool isBoolean = false;
//     for (bsl::size_t i = 0; i < token.size(); ++i) {
//         if (!isdigit(token[i])) {
//             if ('.' == token[i] && !isDouble) {
//                 isDouble = true;
//                 isInteger = false;
//                 continue;
//             }
//             isInteger = false;
//             isDouble = false;
//             break;
//         }
//     }
//
//     if (!isInteger && !isDouble) {
//         if ("true" == token || "false" == token) {
//             isBoolean = true;
//         }
//     }
//
//     if (isInteger) { // integer token
//         return bdld::Datum::createInteger(atoi(token.c_str()));
//     }
//     else if (isDouble) { // double token
//         return bdld::Datum::createDouble(atof(token.c_str()));
//     }
//     else if (isBoolean) { // boolean token
//         return bdld::Datum::createBoolean("true" == token ? true : false);
//     }
//     else { // string value
//         return bdld::Datum::copyString(token, basicAllocator);
//     }
// }
// ```
// Now, in our example main, we tokenize an input string "2.34,4,hi there,true"
// to populate a `Datum` array containing the values '[2.34, 4, "hi there",
// true]':
// ```
// void exampleMain() {
//     bslma::TestAllocator allocator;
//     const bsl::string    input("2.34,4,hi there,true", &allocator);
// ```
// Here, we create a `DatumArrayBuilder`, and iterate over the parsed tokens
// from `input`, using `pushBack` on the array builder to add the tokens to a
// `Datum` array value:
// ```
//     bdld::DatumArrayBuilder builder(0, &allocator);
//
//     bsl::string str(input, &allocator);
//
//     bsl::string value;
//     bsl::size_t nextIndex;
//     do {
//         nextIndex = nextValue(&value, str);
//         builder.pushBack(convertToDatum(value, &allocator));
//         if (bsl::string::npos == nextIndex) {
//             break;
//         }
//         str = str.substr(nextIndex + 1);
//     } while (bsl::string::npos != nextIndex);
//
//     bdld::Datum result = builder.commit();
// ```
// Notice that calling `commit` on the `DatumArrayBuilder` adopts ownership
// for the returned `Datum` object, and that the behavior is undefined if
// `pushBack` is called after `commit`.
//
// Finally, we verify that `result` has the expected array value, and destroy
// the created `Datum` value:
// ```
//     assert(true       == result.isArray());
//     assert(4          == result.theArray().length());
//     assert(true       == result.theArray()[0].isDouble());
//     assert(2.34       == result.theArray()[0].theDouble());
//     assert(true       == result.theArray()[1].isInteger());
//     assert(4          == result.theArray()[1].theInteger());
//     assert(true       == result.theArray()[2].isString());
//     assert("hi there" == result.theArray()[2].theString());
//     assert(true       == result.theArray()[3].isBoolean());
//     assert(true       == result.theArray()[3].theBoolean());
//
//     // Destroy the 'Datum' object.
//     bdld::Datum::destroy(result, &allocator);
// }
// ```

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

/// This `class` provides a mechanism to build a `Datum` object having an
/// array value in an exception-safe manner.
class DatumArrayBuilder {

  public:
    // TYPES

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

    typedef bsl::allocator<char> allocator_type;

  private:
    // DATA
    DatumMutableArrayRef d_array;      // mutable access to the datum array
    SizeType             d_capacity;   // capacity of the datum array
    allocator_type       d_allocator;  // allocator for memory

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

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

    // CREATORS

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

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

    // MANIPULATORS

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

    /// Return a `Datum` array 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 array and
    /// no further values shall be appended.  It is undefined behavior to
    /// call any method of this object, other than the destructor, after
    /// `commit` has been called.
    Datum commit();

    /// Append the specified `value` to the `Datum` array being built 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` has already been called on this object.
    void pushBack(const Datum& value);

    // ACCESSORS

    /// Return the capacity of the held `Datum` array.  The behavior is
    /// undefined if `commit` 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` array being constructed,
    /// but does indicate at which point additional memory will be required
    /// to grow the `Datum` array 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` array.  The behavior is
    /// undefined if `commit` has already been called on this object.
    SizeType size() const;
};

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

                           // -----------------------
                           // class DatumArrayBuilder
                           // -----------------------

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

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

inline
DatumArrayBuilder::SizeType DatumArrayBuilder::size() const
{
    if (d_capacity) {
        return *d_array.length();                                     // RETURN
    }
    return 0;
}

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

#endif

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