// balxml_listparser.h                                                -*-C++-*-

// ----------------------------------------------------------------------------
//                                   NOTICE
//
// This component is not up to date with current BDE coding standards, and
// should not be used as an example for new development.
// ----------------------------------------------------------------------------

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

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

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

  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
    int appendElement(const char *data, int dataLength);
        // Append an element to the associated object having the specified
        // 'data' of the specified 'dataLength'.

  public:
    // CREATORS
    explicit ListParser(ParseElementCallback  parseElementCallback,
                        bslma::Allocator     *basicAllocator = 0);
        // 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.

#ifdef DOXYGEN // Generated by compiler:

    ~ListParser();
        // Destroy this object.

#endif

    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 <class 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.
};

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

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

// PRIVATE MANIPULATORS

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);

    typedef bsl::function<int(ElementType*)> Functor;

    using bdlf::PlaceHolders::_1;

    Functor parseElementFunctor = bdlf::BindUtil::bind(d_parseElementCallback,
                                                       _1,
                                                       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 ----------------------------------