// balxml_listparser.h                                                -*-C++-*-
#ifndef INCLUDED_BALXML_LISTPARSER
#define INCLUDED_BALXML_LISTPARSER

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

//@PURPOSE: Provide push parser for lists.
//
//@CLASSES:
//   balxml::ListParser: push parser for lists
//
//@SEE_ALSO: bdlat_arrayfunctions
//
//@DESCRIPTION: The `balxml::ListParser` class template provided by this
// component can be used to parse lists into an object that supports
// `bdlat_ArrayFunctions`.
//
// This class template is a model of the `PushParser` concept, which contains
// the following methods:
// ```
// int beginParse(TYPE *object);
//     // Prepare the parser to start parsing a new value and associate the
//     // specified 'object' with the parser.  Return 0 if successful and
//     // non-zero otherwise.
//
// int endParse();
//     // Ends the parse operation and store the value parsed from the pushed
//     // characters into the associated object.  Return 0 if successful and
//     // non-zero otherwise.  The behavior is undefined unless an object is
//     // associated with this parser.  Upon successful completion, the parser
//     // will be disassociated with the object.
//
// template <typename INPUT_ITERATOR>
// int pushCharacters(INPUT_ITERATOR begin, INPUT_ITERATOR end);
//     // Push the characters ranging from the specified 'begin' up to (but
//     // not including) the specified 'end' into this parser.  Return 0 if
//     // successful and non-zero otherwise.  The parameterized
//     // 'INPUT_ITERATOR' must be dereferenceable to a 'char' value.  The
//     // behavior is undefined unless an object is associated with this
//     // parser.
// ```
//
///Usage
///-----
// This section illustrates intended use of this component.
//
///Example 1: Basic Usage
/// - - - - - - - - - - -
// The following snippets of code illustrate the usage of this component.
// Suppose you had an input stream that contained a list of doubles.  The
// following `loadDoublesFromListStream` function loads this data into an
// `bsl::vector<double>`:
// ```
// #include <balxml_listparser.h>
//
// #include <bdlt_date.h>
//
// #include <istream>
// #include <iterator>
// #include <vector>
// #include <sstream>
// #include <string>
//
// using namespace BloombergLP;
//
// int parseDouble(double *result, const char *data, int dataLength);
//
// int loadDoublesFromListStream(bsl::vector<double> *result,
//                               bsl::istream&        stream)
// {
//     enum { k_FAILURE = -1 };
//
//     balxml::ListParser<bsl::vector<double> > parser(&parseDouble);
//
//     if (0 != parser.beginParse(result)) {
//         return k_FAILURE;
//     }
//
//     if (0 != parser.pushCharacters(bsl::istreambuf_iterator<char>(stream),
//                                    bsl::istreambuf_iterator<char>())) {
//         return k_FAILURE;
//     }
//
//     return parser.endParse();
// }
// ```
// The `parseDouble` function is implemented as follows:
// ```
// int parseDouble(double *result, const char *data, int dataLength)
// {
//     bsl::stringstream ss(bsl::string(data, dataLength));
//     ss >> (*result);
//     return 0;
// }
// ```
// The following function demonstrates the `loadDoublesFromListStream`
// function:
// ```
// void usageExample()
// {
//     const char INPUT[] = "1.5 2.0 3.8 1.0";
//
//     bsl::vector<double> vec;
//     bsl::istringstream  iss(INPUT);
//
//     int result = loadDoublesFromListStream(&vec, iss);
//
//     assert(0   == result);
//     assert(4   == vec.size());
//     assert(1.5 == vec[0]);
//     assert(2.0 == vec[1]);
//     assert(3.8 == vec[2]);
//     assert(1.0 == vec[3]);
// }
// ```

#include <balscm_version.h>

#include <bdlat_arrayfunctions.h>

#include <bslalg_typetraits.h>

#include <bdlf_bind.h>
#include <bdlf_placeholder.h>

#include <bdlb_chartype.h>

#include <bsls_assert.h>
#include <bsls_review.h>

#include <bsl_functional.h>
#include <bsl_memory.h>
#include <bsl_string.h>

namespace BloombergLP {

namespace balxml {
                           // ======================
                           // class ListParser<TYPE>
                           // ======================

/// This is a push parser for lists.
template <class TYPE>
class ListParser {

    // PRIVATE TYPES
    typedef typename
    bdlat_ArrayFunctions::ElementType<TYPE>::Type ElementType;

    struct ParseElementFunctor;

  public:
    // TYPES
    typedef int (*ParseElementFunction)(ElementType*, const char*, int);
    typedef bsl::function<int(ElementType*, const char*, int)>
                                                          ParseElementCallback;

  private:
    // PRIVATE DATA MEMBERS
    bsl::string           d_characters;            // accumulated characters
    TYPE                 *d_object_p;              // associated object
    ParseElementCallback  d_parseElementCallback;  // callback for parsing
                                                   // elements

    // NOT IMPLEMENTED
    ListParser(const ListParser&);
    ListParser& operator=(const ListParser&);

    // PRIVATE MANIPULATORS

    /// Append an element to the associated object having the specified
    /// `data` of the specified `dataLength`.
    int appendElement(const char *data, int dataLength);

  public:
    // CREATORS

    /// Create a parser for lists using the specified `parseElementCallback`
    /// functor to parse each element and the optionally specified
    /// `basicAllocator` for supplying memory.  If `basicAllocator` is 0,
    /// the currently installed default allocator will be used.
    explicit ListParser(ParseElementCallback  parseElementCallback,
                        bslma::Allocator     *basicAllocator = 0);

#ifdef DOXYGEN // Generated by compiler:

    ~ListParser();
        // Destroy this object.

#endif

    /// Prepare the parser to start parsing a new value and associate the
    /// specified `object` with the parser.  Return 0 if successful and
    /// non-zero otherwise.
    int beginParse(TYPE *object);

    /// Ends the parse operation and store the value parsed from the pushed
    /// characters into the associated object.  Return 0 if successful and
    /// non-zero otherwise.  The behavior is undefined unless an object is
    /// associated with this parser.  Upon successful completion, the parser
    /// will be disassociated with the object.
    int endParse();

    /// Push the characters ranging from the specified `begin` up to (but
    /// not including) the specified `end` into this parser.  Return 0 if
    /// successful and non-zero otherwise.  The parameterized
    /// `INPUT_ITERATOR` must be dereferenceable to a `char` value.  The
    /// behavior is undefined unless an object is associated with this
    /// parser.
    template <class INPUT_ITERATOR>
    int pushCharacters(INPUT_ITERATOR begin, INPUT_ITERATOR end);
};

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

                           // ----------------------
                           // class ListParser<TYPE>
                           // ----------------------

// PRIVATE MANIPULATORS

template <class TYPE>
struct ListParser<TYPE>::ParseElementFunctor {
    // PUBLIC DATA
    ParseElementCallback&  d_parseElementCallback;
    const char            *d_data;
    int                    d_dataLength;

    // MANIPULATORS
    int operator()(ElementType *elem)
    {
        return d_parseElementCallback(elem, d_data, d_dataLength);
    }
    template <class t_ELEM>
    int operator()(t_ELEM *)
    {
        return -1;
    }
};

template <class TYPE>
int ListParser<TYPE>::appendElement(const char *data, int dataLength)
{
    BSLS_ASSERT(data);
    BSLS_ASSERT(0 < dataLength);

    enum { k_SUCCESS = 0, k_FAILURE = -1 };

    const int i = static_cast<int>(bdlat_ArrayFunctions::size(*d_object_p));

    bdlat_ArrayFunctions::resize(d_object_p, i + 1);

    ParseElementFunctor parseElementFunctor = {d_parseElementCallback,
                                               data,
                                               dataLength};

    if (0 != bdlat_ArrayFunctions::manipulateElement(d_object_p,
                                                     parseElementFunctor,
                                                     i)) {
        // remove the new object from the array
        bdlat_ArrayFunctions::resize(d_object_p, i);

        return k_FAILURE;                                             // RETURN
    }

     return k_SUCCESS;
}

// CREATORS

template <class TYPE>
ListParser<TYPE>::ListParser(ParseElementCallback  parseElementCallback,
                             bslma::Allocator     *basicAllocator)
: d_characters(basicAllocator)
, d_object_p(0)
, d_parseElementCallback(bsl::allocator_arg_t(),
                         bsl::allocator<ParseElementCallback>(basicAllocator),
                         parseElementCallback)
{
}

// MANIPULATORS

template <class TYPE>
int ListParser<TYPE>::beginParse(TYPE *object)
{
    BSLS_ASSERT(object);

    enum { k_SUCCESS = 0 };

    d_characters.clear();
    d_object_p = object;

    bdlat_ArrayFunctions::resize(d_object_p, 0);

    return k_SUCCESS;
}

template <class TYPE>
int ListParser<TYPE>::endParse()
{
    BSLS_ASSERT(d_object_p);

    enum { k_SUCCESS = 0, k_FAILURE = -1 };

    if (!d_characters.empty()) {
        if (0 != appendElement(d_characters.data(),
                               static_cast<int>(d_characters.length()))) {
            return k_FAILURE;                                         // RETURN
        }
    }

    d_object_p = 0;

    return k_SUCCESS;
}

template <class TYPE>
template <class INPUT_ITERATOR>
int ListParser<TYPE>::pushCharacters(INPUT_ITERATOR begin, INPUT_ITERATOR end)
{
    BSLS_ASSERT(d_object_p);

    enum { k_SUCCESS = 0, k_FAILURE = -1 };

    while (begin != end) {
        const char character = *begin;

        ++begin;

        if (bdlb::CharType::isSpace(character)) {
            if (!d_characters.empty()) {
                if (0 != appendElement(
                                    d_characters.data(),
                                    static_cast<int>(d_characters.length()))) {
                    return k_FAILURE;                                 // RETURN
                }

                d_characters.clear();
            }
        }
        else {
            d_characters.push_back(character);
        }
    }

    return k_SUCCESS;
}

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

#endif // ! defined(INCLUDED_BAEXML_LISTPARSER)

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