// bdlat_symbolicconverter.h                                          -*-C++-*-
#ifndef INCLUDED_BDLAT_SYMBOLICCONVERTER
#define INCLUDED_BDLAT_SYMBOLICCONVERTER

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

//@PURPOSE: Provide a utility for convert types with matching member symbols.
//
//@CLASSES:
//  bdlat_SymbolicConverter: symbolic converter utility
//
//@SEE_ALSO:
//
//@DESCRIPTION: The `bdlat_SymbolicConverter` utility provided by this
// component defines a single parameterized function `convert`.  The `convert`
// function takes two arguments: a destination and a source object.  The
// destination and source objects may be of different types.
//
// Each type can fall into one of the following categories:
// ```
// Category          Reference
// --------          ---------
// Sequence          bdlat_sequencefunctions
// Choice            bdlat_choicefunctions
// Array             bdlat_arrayfunctions
// Enumeration       bdlat_enumfunctions
// NullableValue     bdlat_nullablevaluefunctions
// CustomizedType    bdlat_customizedtypefunctions
// Simple            basic C++ fundamental types & other value-semantic types
// ```
// The `bdlat_SymbolicConverter` utility converts from one type to another
// using the following criteria:
// ```
// Destination Category  Source Category   Comments
// --------------------  ---------------   --------
// Sequence              Sequence          The conversion will fail if each
//                                         attribute in the set of attributes
//                                         from the source does not have a
//                                         corresponding attribute (with the
//                                         same name) in the destination.  The
//                                         conversion will also fail if any
//                                         attributes from the source fail to
//                                         convert to the corresponding
//                                         attribute in the destination.  Any
//                                         attribute in the destination that
//                                         does not have a corresponding
//                                         attribute in the source will be set
//                                         to its default value.
//
// Choice                Choice            The conversion will fail if the
//                                         destination does not have a
//                                         selection with the same name as the
//                                         current selection in the source.
//                                         The conversion will also fail if
//                                         the selection from the source fails
//                                         to convert to the corresponding
//                                         selection in the destination.  If
//                                         nothing is selected in the source,
//                                         then the destination will be reset.
//
// Array                 Array             The conversion will fail if the
//                                         elements in the source fail to
//                                         convert to the elements in the
//                                         destination.  Upon completion, the
//                                         destination array will contain the
//                                         same number of elements as the
//                                         source array.
//
// Enumeration           Enumeration       The conversion will fail if the
//                                         destination does not have a string
//                                         value that is identical to the
//                                         string value of the source.
//
// Enumeration           char/short/int    The conversion will fail if the
//                                         destination does not have an
//                                         enumerator with the numeric value
//                                         of the source.
//
// char/short/int        Enumeration       The conversion will fail if the
//                                         numeric value of the enumeration is
//                                         outside the bounds of the
//                                         destination type.
//
// Enumeration           bsl::string       The conversion will fail if the
//                                         destination does not have an
//                                         enumerator with the symbolic string
//                                         name of the source.
//
// bsl::string           Enumeration       This conversion always succeeds.
//
// NullableValue         NullableValue     The conversion will fail if the
//                                         source has a value that fails to
//                                         convert to the destination value.
//                                         If the source is null, then the
//                                         destination is nulled.
//
// NullableValue         AnyType           The conversion will fail if the
//                                         source fails to convert to the
//                                         destination value.
//
// AnyType               NullableValue     The conversion will fail if the
//                                         source is not null and the value in
//                                         the source fails to convert to the
//                                         destination.  If the source is
//                                         null, then the destination will
//                                         be set to its default value.
//
// CustomizedType        CustomizedType    The conversion will fail if the
//                                         base value in the source fails to
//                                         convert to the base value in the
//                                         destination and the base value is
//                                         able to convert to the customized
//                                         value.
//
// CustomizedType        AnyType           The conversion will fail if the
//                                         source fails to convert to the base
//                                         value in the destination and the
//                                         base value is able to convert to
//                                         the customized value.
//
// AnyType               CustomizedType    The conversion will fail if the
//                                         base value in the source fails to
//                                         convert to the destination.
//
// SimpleType            SimpleType        The conversion will fail if there
//                                         is no accessible compile-time
//                                         assignment operator from the
//                                         destination to the source.  This is
//                                         determined using
//                                         `bslmf_isconvertible`.
// ```
// Any other combination of destination and source categories will fail to
// convert.
//
///Usage
///-----
// This section illustrates intended use of this component.
//
///Example 1: Basic Usage
/// - - - - - - - - - - -
// This component can be used with types supported by the `bdlat` framework.
// In particular, types generated by the `bas_codegen.pl` tool can be used.
// For example, suppose we have the following XML schema inside a file called
// `xsdfile.xsd`:
// ```
// <?xml version='1.0' encoding='UTF-8'?>
// <xs:schema xmlns:xs='http://www.w3.org/2001/XMLSchema'
//            xmlns:bdem='http://bloomberg.com/schemas/bdem'
//            elementFormDefault='unqualified'>
//
//     <xs:complexType name='Employee'>
//         <xs:sequence>
//             <xs:element name='Name'   type='string'/>
//             <xs:element name='Dept'   type='string'/>
//             <xs:element name='Age'    type='int'/>
//             <xs:element name='Salary' type='float'/>
//         </xs:sequence>
//     </xs:complexType>
//
//     <xs:complexType name='Trainee'>
//         <xs:sequence>
//             <xs:element name='Name' type='string'/>
//             <xs:element name='Dept' type='string'/>
//             <xs:element name='Age'  type='int'/>
//         </xs:sequence>
//     </xs:complexType>
//
// </xs:schema>
// ```
// Using the `bas_codegen.pl` tool, we can generate C++ classes for this
// schema:
// ```
// $ bas_codegen.pl -g h -g cpp -p test xsdfile.xsd
// ```
// This tool will generate the header and implementation files for the
// `test_employee` and `test_trainee` components in the current directory.
//
// Now suppose we want to create a `hireTrainee` function, that converts a
// trainee to an employee.  Such a function could be written as follows:
// ```
// #include <test_employee.h>
// #include <test_trainee.h>
//
// #include <bdlat_symbolicconverter.h>
//
// using namespace BloombergLP;
//
// int hireTrainee(test::Employee       *result,
//                 const test::Trainee&  trainee,
//                 float                 salary)
// {
//     int retCode = bdlat_SymbolicConverter::convert(result, trainee);
//
//     result->salary() = salary;
//
//     return retCode;
// }
// ```
//  The `hireTrainee` function can be used as follows:
// ```
// void usageExample()
// {
//     test::Trainee trainee;
//
//     trainee.name() = "Bob";
//     trainee.dept() = "RnD";
//     trainee.age()  = 24;
//
//     test::Employee employee;
//
//     int result = hireTrainee(&employee, trainee, 20000.00f);
//
//     assert(0         == result);
//     assert("Bob"     == employee.name());
//     assert("RnD"     == employee.dept());
//     assert(24        == employee.age());
//     assert(20000.00f == employee.salary());
// }
// ```

#include <bdlscm_version.h>

#include <bdlat_arrayfunctions.h>
#include <bdlat_bdeatoverrides.h>
#include <bdlat_choicefunctions.h>
#include <bdlat_customizedtypefunctions.h>
#include <bdlat_enumfunctions.h>
#include <bdlat_nullablevaluefunctions.h>
#include <bdlat_sequencefunctions.h>
#include <bdlat_typecategory.h>
#include <bdlat_valuetypefunctions.h>

#include <bdlb_printmethods.h>

#include <bslmf_switch.h>

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

#include <bsl_ostream.h>
#include <bsl_string.h>

namespace BloombergLP {

                       // ==============================
                       // struct bdlat_SymbolicConverter
                       // ==============================

/// This utility contains a single `convert` function that converts a value
/// from one type to another compatible type.
struct bdlat_SymbolicConverter {

    /// Convert the value of the specified `rhs` object to the specified
    /// (modifiable) `lhs` object.  Optionally specify an `errorStream` to
    /// print error messages.  Return 0 on success and a non-zero value
    /// otherwise.  The supported conversions are described in the
    /// `bdlat_symbolicconverter` component-level documentation.
    template <class LHS_TYPE, class RHS_TYPE>
    static
    int convert(LHS_TYPE *lhs, const RHS_TYPE &rhs);
    template <class LHS_TYPE, class RHS_TYPE>
    static
    int convert(LHS_TYPE *lhs, const RHS_TYPE &rhs, bsl::ostream &errorStream);
};

// ---  Anything below this line is implementation specific.  Do not use.  ----

                     // =================================
                     // class bdlat_SymbolicConverter_Imp
                     // =================================

/// This class contains implementation functions for this component.
class bdlat_SymbolicConverter_Imp {
    // PRIVATE TYPES
    template <class RHS_TYPE>
    struct CustomizedManipulator;

    // PRIVATE DATA MEMBERS
    bsl::ostream *d_errorStream_p;  // held, not owned

  public:
    // IMPLEMENTATION MANIPULATORS

    /// Convert to sequence from sequence.
    template <class LHS_TYPE, class RHS_TYPE>
    int convert(LHS_TYPE                     *lhs,
                bdlat_TypeCategory::Sequence  lhsCategory,
                const RHS_TYPE&               rhs,
                bdlat_TypeCategory::Sequence  rhsCategory);

    /// Convert to choice from choice.
    template <class LHS_TYPE, class RHS_TYPE>
    int convert(LHS_TYPE                   *lhs,
                bdlat_TypeCategory::Choice  lhsCategory,
                const RHS_TYPE&             rhs,
                bdlat_TypeCategory::Choice  rhsCategory);

    /// Convert to array from array.
    template <class LHS_TYPE, class RHS_TYPE>
    int convert(LHS_TYPE                  *lhs,
                bdlat_TypeCategory::Array  lhsCategory,
                const RHS_TYPE&            rhs,
                bdlat_TypeCategory::Array  rhsCategory);

    /// Convert to enum from enum.
    template <class LHS_TYPE, class RHS_TYPE>
    int convert(LHS_TYPE                        *lhs,
                bdlat_TypeCategory::Enumeration  lhsCategory,
                const RHS_TYPE&                  rhs,
                bdlat_TypeCategory::Enumeration  rhsCategory);

    /// Convert to enum from simple type.
    template <class LHS_TYPE, class RHS_TYPE>
    int convert(LHS_TYPE                        *lhs,
                bdlat_TypeCategory::Enumeration  lhsCategory,
                const RHS_TYPE&                  rhs,
                bdlat_TypeCategory::Simple       rhsCategory);

    /// Convert to simple type from enum.
    template <class LHS_TYPE, class RHS_TYPE>
    int convert(LHS_TYPE                        *lhs,
                bdlat_TypeCategory::Simple       lhsCategory,
                const RHS_TYPE&                  rhs,
                bdlat_TypeCategory::Enumeration  rhsCategory);

    /// Convert to nullable from nullable.
    template <class LHS_TYPE, class RHS_TYPE>
    int convert(LHS_TYPE                          *lhs,
                bdlat_TypeCategory::NullableValue  lhsCategory,
                const RHS_TYPE&                    rhs,
                bdlat_TypeCategory::NullableValue  rhsCategory);

    /// Convert to nullable from non-nullable.
    template <class LHS_TYPE, class RHS_TYPE, class RHS_CATEGORY>
    int convert(LHS_TYPE                          *lhs,
                bdlat_TypeCategory::NullableValue  lhsCategory,
                const RHS_TYPE&                    rhs,
                RHS_CATEGORY                       rhsCategory);

    /// Convert to non-nullable from nullable.
    template <class LHS_TYPE, class LHS_CATEGORY, class RHS_TYPE>
    int convert(LHS_TYPE                          *lhs,
                LHS_CATEGORY                       lhsCategory,
                const RHS_TYPE&                    rhs,
                bdlat_TypeCategory::NullableValue  rhsCategory);

    /// Convert to customized type from nullable.  Note that this overload
    /// is required to resolve ambiguities when there are nullable and
    /// customized types in the same sequence.
    template <class LHS_TYPE, class RHS_TYPE>
    int convert(LHS_TYPE                           *lhs,
                bdlat_TypeCategory::CustomizedType  lhsCategory,
                const RHS_TYPE&                     rhs,
                bdlat_TypeCategory::NullableValue   rhsCategory);

    /// Convert to customized from customized.
    template <class LHS_TYPE, class RHS_TYPE>
    int convert(LHS_TYPE                           *lhs,
                bdlat_TypeCategory::CustomizedType  lhsCategory,
                const RHS_TYPE&                     rhs,
                bdlat_TypeCategory::CustomizedType  rhsCategory);

    /// Convert to customized from non-customized.
    template <class LHS_TYPE, class RHS_TYPE, class RHS_CATEGORY>
    int convert(LHS_TYPE                           *lhs,
                bdlat_TypeCategory::CustomizedType  lhsCategory,
                const RHS_TYPE&                     rhs,
                RHS_CATEGORY                        rhsCategory);

    /// Convert to non-customized from customized.
    template <class LHS_TYPE, class LHS_CATEGORY, class RHS_TYPE>
    int convert(LHS_TYPE                           *lhs,
                LHS_CATEGORY                        lhsCategory,
                const RHS_TYPE&                     rhs,
                bdlat_TypeCategory::CustomizedType  rhsCategory);

    /// Convert to nullable from customized.  Note that this overload is
    /// required to resolve ambiguities when there are nullable and
    /// customized types in the same sequence.
    template <class LHS_TYPE, class RHS_TYPE>
    int convert(LHS_TYPE                           *lhs,
                bdlat_TypeCategory::NullableValue   lhsCategory,
                const RHS_TYPE&                     rhs,
                bdlat_TypeCategory::CustomizedType  rhsCategory);

    /// Convert to simple from simple of the same type.  Note that this just
    /// evaluates to an assignment using the assignment operator.
    template <class LHS_TYPE, class RHS_TYPE>
    int convert(LHS_TYPE                   *lhs,
                bdlat_TypeCategory::Simple  lhsCategory,
                const RHS_TYPE&             rhs,
                bdlat_TypeCategory::Simple  rhsCategory);

    /// No match found.  This function does nothing, it just returns a
    /// FAILURE code (to be used to detect type-mismatch at runtime).
    template <class LHS_TYPE,
              class LHS_CATEGORY,
              class RHS_TYPE,
              class RHS_CATEGORY>
    int convert(LHS_TYPE        *lhs,
                LHS_CATEGORY     lhsCategory,
                const RHS_TYPE&  rhs,
                RHS_CATEGORY     rhsCategory);

    /// Resolve dynamic types.
    template <class LHS_TYPE, class RHS_TYPE>
    int resolveDynamicTypes(LHS_TYPE                        *lhs,
                            bdlat_TypeCategory::DynamicType  lhsCategory,
                            const RHS_TYPE&                  rhs,
                            bdlat_TypeCategory::DynamicType  rhsCategory);
    template <class LHS_TYPE,
              class RHS_TYPE,
              class RHS_CATEGORY>
    int resolveDynamicTypes(LHS_TYPE                        *lhs,
                            bdlat_TypeCategory::DynamicType  lhsCategory,
                            const RHS_TYPE&                  rhs,
                            RHS_CATEGORY                     rhsCategory);
    template <class LHS_TYPE,
              class LHS_CATEGORY,
              class RHS_TYPE>
    int resolveDynamicTypes(LHS_TYPE                        *lhs,
                            LHS_CATEGORY                     lhsCategory,
                            const RHS_TYPE&                  rhs,
                            bdlat_TypeCategory::DynamicType  rhsCategory);
    template <class LHS_TYPE,
              class LHS_CATEGORY,
              class RHS_TYPE,
              class RHS_CATEGORY>
    int resolveDynamicTypes(LHS_TYPE        *lhs,
                            LHS_CATEGORY     lhsCategory,
                            const RHS_TYPE&  rhs,
                            RHS_CATEGORY     rhsCategory);

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

  public:
    // CREATORS

    /// Create the imp object.
    bdlat_SymbolicConverter_Imp(bsl::ostream *errorStream);

    /// Destroy this object.
    //! ~bdlat_SymbolicConverter_Imp() = default;

    // MANIPULATORS

    /// Implementation for convert function.
    template <class LHS_TYPE, class RHS_TYPE>
    int convert(LHS_TYPE        *lhs,
                const RHS_TYPE&  rhs);

    /// Return a reference to the error stream.
    bsl::ostream& errorStream();
};

           // =====================================================
           // class bdlat_SymbolicConverter_StoreValue<LVALUE_TYPE>
           // =====================================================

/// This visitor assigns the value of the visited member to
/// `d_destination_p`.
template <class LVALUE_TYPE>
class bdlat_SymbolicConverter_StoreValue {

    // PRIVATE DATA MEMBERS
    LVALUE_TYPE                 *d_destination_p;  // held, not owned
    bdlat_SymbolicConverter_Imp *d_imp_p;          // held, not owned

  public:
    // CREATORS
    explicit bdlat_SymbolicConverter_StoreValue(
                                      LVALUE_TYPE                 *destination,
                                      bdlat_SymbolicConverter_Imp *imp);

    // ACCESSORS

    /// Assign the specified `object` to `*d_lValue_p`.
    template <class RVALUE_TYPE, class INFO_TYPE>
    int operator()(const RVALUE_TYPE& object,
                   const INFO_TYPE&) const;

    /// Assign the specified `object` to `*d_lValue_p`.
    template <class RVALUE_TYPE>
    int operator()(const RVALUE_TYPE& object) const;
};

            // ====================================================
            // class bdlat_SymbolicConverter_LoadValue<RVALUE_TYPE>
            // ====================================================

/// This visitor assigns `d_value` to the visited member.
template <class RVALUE_TYPE>
class bdlat_SymbolicConverter_LoadValue {

    // PRIVATE DATA MEMBERS
    bdlat_SymbolicConverter_Imp *d_imp_p;  // held, not owned
    const RVALUE_TYPE&           d_value;  // held, not owned

  public:
    // CREATORS
    explicit bdlat_SymbolicConverter_LoadValue(
                                            const RVALUE_TYPE&           value,
                                            bdlat_SymbolicConverter_Imp *imp);

    // ACCESSORS

    /// Assign `d_value` to the specified `*object`.
    template <class LVALUE_TYPE, class INFO_TYPE>
    int operator()(LVALUE_TYPE *object,
                   const INFO_TYPE&) const;

    /// Assign `d_value` to the specified `*object`.
    template <class LVALUE_TYPE>
    int operator()(LVALUE_TYPE *object) const;
};

        // ============================================================
        // class bdlat_SymbolicConverter_StoreInSequence<SEQUENCE_TYPE>
        // ============================================================

/// This visitor is used when assigning to a sequence.  It will visit each
/// member from the source object.  Each time a member is visited, it will
/// use the `LoadValue` visitor template to visit the member with the same
/// name in the destination sequence.  This will cause the value of the
/// member in the source object to be assigned to the member (with the same
/// name) of the destination sequence object.
template <class SEQUENCE_TYPE>
class bdlat_SymbolicConverter_StoreInSequence {

    // PRIVATE DATA MEMBERS
    SEQUENCE_TYPE               *d_destination_p;  // held, not owned
    bdlat_SymbolicConverter_Imp *d_imp_p;          // held, not owned

  public:
    // CREATORS
    explicit bdlat_SymbolicConverter_StoreInSequence(
                                      SEQUENCE_TYPE               *destination,
                                      bdlat_SymbolicConverter_Imp *imp);

    // ACCESSORS
    template <class SOURCE_MEMBER_TYPE, class INFO_TYPE>
    int operator()(const SOURCE_MEMBER_TYPE& sourceMember,
                   const INFO_TYPE&          info) const;
};

          // ========================================================
          // class bdlat_SymbolicConverter_StoreInChoice<CHOICE_TYPE>
          // ========================================================

/// Similar to `StoreInSequence` but this is for choice.
template <class CHOICE_TYPE>
class bdlat_SymbolicConverter_StoreInChoice {

    // PRIVATE DATA MEMBERS
    CHOICE_TYPE                 *d_destination_p;  // held, not owned
    bdlat_SymbolicConverter_Imp *d_imp_p;          // held, not owned

  public:
    // CREATORS
    explicit bdlat_SymbolicConverter_StoreInChoice(
                                      CHOICE_TYPE                 *destination,
                                      bdlat_SymbolicConverter_Imp *imp);

    // ACCESSORS
    template <class SOURCE_MEMBER_TYPE, class INFO_TYPE>
    int operator()(const SOURCE_MEMBER_TYPE& sourceMember,
                   const INFO_TYPE&          info) const;
};

       // =============================================================
       // class bdlat_SymbolicConverter_StoreInArrayElement<ARRAY_TYPE>
       // =============================================================

/// Assign the value of the visited object to the `d_index`th element inside
/// `d_array_p`.
template <class ARRAY_TYPE>
class bdlat_SymbolicConverter_StoreInArrayElement {

    // PRIVATE DATA MEMBERS
    ARRAY_TYPE                  *d_array_p;  // held, not owned
    bdlat_SymbolicConverter_Imp *d_imp_p;    // held, not owned
    int                          d_index;    // element index to assign to

  public:
    // CREATORS
    bdlat_SymbolicConverter_StoreInArrayElement(
                                            ARRAY_TYPE                  *array,
                                            int                          index,
                                            bdlat_SymbolicConverter_Imp *imp);

    // ACCESSORS
    template <class SOURCE_ELEMENT_TYPE>
    int operator()(const SOURCE_ELEMENT_TYPE& sourceElement) const;
};

        // ============================================================
        // class bdlat_SymbolicConverter_StoreInNullable<NULLABLE_TYPE>
        // ============================================================

/// Similar to `StoreInSequence` but this is for nullable.
template <class NULLABLE_TYPE>
class bdlat_SymbolicConverter_StoreInNullable {

    // PRIVATE DATA MEMBERS
    NULLABLE_TYPE               *d_destination_p;  // held, not owned
    bdlat_SymbolicConverter_Imp *d_imp_p;          // held, not owned

  public:
    // CREATORS
    explicit bdlat_SymbolicConverter_StoreInNullable(
                                      NULLABLE_TYPE               *destination,
                                      bdlat_SymbolicConverter_Imp *imp);

    // ACCESSORS
    template <class VALUE_TYPE>
    int operator()(const VALUE_TYPE& value) const;
};

// ============================================================================
//                               PROXY CLASSES
// ============================================================================

         // =========================================================
         // struct bdlat_SymbolicConverter_Imp_resolveDynamicRhsProxy
         // =========================================================

/// Component-private struct.  Do not use.
template <class LHS_TYPE, class LHS_CATEGORY>
struct bdlat_SymbolicConverter_Imp_resolveDynamicRhsProxy {

    // DATA MEMBERS
    bdlat_SymbolicConverter_Imp *d_instance_p;
    LHS_TYPE                    *d_lhs_p;

    // CREATORS

    // Creators have been omitted to allow simple static initialization of this
    // struct.

    // FUNCTIONS
    template <class TYPE>
    inline
    int operator()(const TYPE&, bslmf::Nil)
    {
        BSLS_ASSERT_SAFE(0);
        return -2;
    }

    template <class TYPE, class ANY_CATEGORY>
    inline
    int operator()(const TYPE& object, ANY_CATEGORY category)
    {
        return d_instance_p->resolveDynamicTypes(d_lhs_p,
                                                 LHS_CATEGORY(),
                                                 object,
                                                 category);
    }
};

         // =========================================================
         // struct bdlat_SymbolicConverter_Imp_resolveDynamicLhsProxy
         // =========================================================

/// Component-private struct.  Do not use.
template <class RHS_TYPE, class RHS_CATEGORY>
struct bdlat_SymbolicConverter_Imp_resolveDynamicLhsProxy {

    // DATA MEMBERS
    bdlat_SymbolicConverter_Imp *d_instance_p;
    const RHS_TYPE              *d_rhs_p;

    // CREATORS

    // Creators have been omitted to allow simple static initialization of this
    // struct.

    // FUNCTIONS
    template <class TYPE>
    inline
    int operator()(TYPE *, bslmf::Nil)
    {
        BSLS_ASSERT_SAFE(0);
        return -3;
    }

    template <class TYPE, class ANY_CATEGORY>
    inline
    int operator()(TYPE *object, ANY_CATEGORY category)
    {
        return d_instance_p->resolveDynamicTypes(object,
                                                 category,
                                                 *d_rhs_p,
                                                 RHS_CATEGORY());
    }
};

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

                     // ---------------------------------
                     // class bdlat_SymbolicConverter_Imp
                     // ---------------------------------

// PRIVATE MANIPULATORS

template <class LHS_TYPE, class RHS_TYPE>
inline
int bdlat_SymbolicConverter_Imp::convert(LHS_TYPE                      *lhs,
                                         bdlat_TypeCategory::Sequence,
                                         const RHS_TYPE&                rhs,
                                         bdlat_TypeCategory::Sequence)
{
    bdlat_SymbolicConverter_StoreInSequence<LHS_TYPE> storeInLhs(lhs, this);

    return bdlat_SequenceFunctions::accessAttributes(rhs, storeInLhs);
}

template <class LHS_TYPE, class RHS_TYPE>
int bdlat_SymbolicConverter_Imp::convert(LHS_TYPE                    *lhs,
                                         bdlat_TypeCategory::Choice,
                                         const RHS_TYPE&              rhs,
                                         bdlat_TypeCategory::Choice)
{
    enum { k_SUCCESS = 0 };

    bdlat_SymbolicConverter_StoreInChoice<LHS_TYPE> storeInLhs(lhs, this);

    if (bdlat_ChoiceFunctions::k_UNDEFINED_SELECTION_ID
                                  == bdlat_ChoiceFunctions::selectionId(rhs)) {
        bdlat_ValueTypeFunctions::reset(lhs);

        return k_SUCCESS;                                             // RETURN
    }

    return bdlat_ChoiceFunctions::accessSelection(rhs, storeInLhs);
}

template <class LHS_TYPE, class RHS_TYPE>
int bdlat_SymbolicConverter_Imp::convert(LHS_TYPE                   *lhs,
                                         bdlat_TypeCategory::Array,
                                         const RHS_TYPE&             rhs,
                                         bdlat_TypeCategory::Array)
{
    enum { k_SUCCESS = 0, k_FAILURE = -4 };

    const int size = static_cast<int>(bdlat_ArrayFunctions::size(rhs));

    bdlat_ArrayFunctions::resize(lhs, size);

    for (int i = 0; i < size; ++i) {
        bdlat_SymbolicConverter_StoreInArrayElement<LHS_TYPE>
                                                      storeInLhs(lhs, i, this);

        if (0 != bdlat_ArrayFunctions::accessElement(rhs, storeInLhs, i)) {
            return k_FAILURE;                                         // RETURN
        }
    }

    return k_SUCCESS;
}

template <class LHS_TYPE, class RHS_TYPE>
inline
int bdlat_SymbolicConverter_Imp::convert(LHS_TYPE                         *lhs,
                                         bdlat_TypeCategory::Enumeration,
                                         const RHS_TYPE&                   rhs,
                                         bdlat_TypeCategory::Enumeration)
{
    bsl::string str;

    bdlat_EnumFunctions::toString(&str, rhs);

    return bdlat_EnumFunctions::fromString(lhs,
                                           str.data(),
                                           static_cast<int>(str.length()));
}

template <class LHS_TYPE, class RHS_TYPE>
inline
int bdlat_SymbolicConverter_Imp::convert(LHS_TYPE                         *lhs,
                                         bdlat_TypeCategory::Enumeration,
                                         const RHS_TYPE&                   rhs,
                                         bdlat_TypeCategory::Simple)
{
    return bdlat_ValueTypeFunctions::assign(lhs, rhs);
}

template <class LHS_TYPE, class RHS_TYPE>
inline
int bdlat_SymbolicConverter_Imp::convert(LHS_TYPE                         *lhs,
                                         bdlat_TypeCategory::Simple,
                                         const RHS_TYPE&                   rhs,
                                         bdlat_TypeCategory::Enumeration)
{
    return bdlat_ValueTypeFunctions::assign(lhs, rhs);
}

template <class LHS_TYPE, class RHS_TYPE>
int bdlat_SymbolicConverter_Imp::convert(
                                       LHS_TYPE                           *lhs,
                                       bdlat_TypeCategory::NullableValue,
                                       const RHS_TYPE&                     rhs,
                                       bdlat_TypeCategory::NullableValue)
{
    enum { k_SUCCESS = 0 };

    if (bdlat_NullableValueFunctions::isNull(rhs)) {
        bdlat_ValueTypeFunctions::reset(lhs);

        return k_SUCCESS;                                             // RETURN
    }

    bdlat_SymbolicConverter_StoreInNullable<LHS_TYPE> storeInLhs(lhs, this);

    return bdlat_NullableValueFunctions::accessValue(rhs, storeInLhs);
}

template <class LHS_TYPE, class RHS_TYPE, class RHS_CATEGORY>
inline
int bdlat_SymbolicConverter_Imp::convert(
                                       LHS_TYPE                           *lhs,
                                       bdlat_TypeCategory::NullableValue,
                                       const RHS_TYPE&                     rhs,
                                       RHS_CATEGORY)
{
    bdlat_SymbolicConverter_StoreInNullable<LHS_TYPE> storeInLhs(lhs, this);

    return storeInLhs(rhs);
}

template <class LHS_TYPE, class LHS_CATEGORY, class RHS_TYPE>
int bdlat_SymbolicConverter_Imp::convert(
                                       LHS_TYPE                           *lhs,
                                       LHS_CATEGORY,
                                       const RHS_TYPE&                     rhs,
                                       bdlat_TypeCategory::NullableValue)
{
    enum { k_SUCCESS = 0 };

    if (bdlat_NullableValueFunctions::isNull(rhs)) {
        // ignore the value and let `*lhs` contain its *default* value
        return k_SUCCESS;                                             // RETURN
    }

    bdlat_SymbolicConverter_StoreValue<LHS_TYPE> storeIntoLhs(lhs, this);

    return bdlat_NullableValueFunctions::accessValue(rhs, storeIntoLhs);
}

template <class LHS_TYPE, class RHS_TYPE>
int bdlat_SymbolicConverter_Imp::convert(
                                      LHS_TYPE                            *lhs,
                                      bdlat_TypeCategory::CustomizedType,
                                      const RHS_TYPE&                      rhs,
                                      bdlat_TypeCategory::NullableValue)
{
    enum { k_SUCCESS = 0 };

    if (bdlat_NullableValueFunctions::isNull(rhs)) {
        // ignore the value and let `*lhs` contain its *default* value
        return k_SUCCESS;                                             // RETURN
    }

    bdlat_SymbolicConverter_StoreValue<LHS_TYPE> storeIntoLhs(lhs, this);

    return bdlat_NullableValueFunctions::accessValue(rhs, storeIntoLhs);
}

template <class LHS_TYPE, class RHS_TYPE>
inline
int bdlat_SymbolicConverter_Imp::convert(
                                      LHS_TYPE                            *lhs,
                                      bdlat_TypeCategory::CustomizedType,
                                      const RHS_TYPE&                      rhs,
                                      bdlat_TypeCategory::CustomizedType)
{
    return convert(lhs,
                   bdlat_CustomizedTypeFunctions::convertToBaseType(rhs));
}

template <class RHS_TYPE>
struct bdlat_SymbolicConverter_Imp::CustomizedManipulator {
    // PUBLIC DATA
    bdlat_SymbolicConverter_Imp *d_that;
    const RHS_TYPE&              d_rhs;

    // MANIPULATORS
    template <class LHS_BASE_TYPE>
    int operator()(LHS_BASE_TYPE *lhsBaseValue) {
        enum { k_FAILURE = -5 };
        if (0 != d_that->convert(lhsBaseValue, d_rhs)) {
            return k_FAILURE;                                         // RETURN
        }
        return 0;
    }
};

template <class LHS_TYPE, class RHS_TYPE, class RHS_CATEGORY>
int bdlat_SymbolicConverter_Imp::convert(
                                      LHS_TYPE                            *lhs,
                                      bdlat_TypeCategory::CustomizedType,
                                      const RHS_TYPE&                      rhs,
                                      RHS_CATEGORY)
{
    CustomizedManipulator<RHS_TYPE> baseManipulator = {this, rhs};
    return bdlat_CustomizedTypeFunctions::createBaseAndConvert(
                                                              lhs,
                                                              baseManipulator);
}

template <class LHS_TYPE, class LHS_CATEGORY, class RHS_TYPE>
inline
int bdlat_SymbolicConverter_Imp::convert(
                                      LHS_TYPE                            *lhs,
                                      LHS_CATEGORY,
                                      const RHS_TYPE&                      rhs,
                                      bdlat_TypeCategory::CustomizedType)
{
    return convert(lhs,
                   bdlat_CustomizedTypeFunctions::convertToBaseType(rhs));
}

template <class LHS_TYPE, class RHS_TYPE>
inline
int bdlat_SymbolicConverter_Imp::convert(
                                      LHS_TYPE                            *lhs,
                                      bdlat_TypeCategory::NullableValue,
                                      const RHS_TYPE&                      rhs,
                                      bdlat_TypeCategory::CustomizedType)
{
    return convert(lhs,
                   bdlat_CustomizedTypeFunctions::convertToBaseType(rhs));
}

template <class LHS_TYPE, class RHS_TYPE>
inline
int bdlat_SymbolicConverter_Imp::convert(LHS_TYPE                    *lhs,
                                         bdlat_TypeCategory::Simple,
                                         const RHS_TYPE&              rhs,
                                         bdlat_TypeCategory::Simple)
{
    return bdlat_ValueTypeFunctions::assign(lhs, rhs);
}

template <class LHS_TYPE,
          class LHS_CATEGORY,
          class RHS_TYPE,
          class RHS_CATEGORY>
inline
int bdlat_SymbolicConverter_Imp::convert(LHS_TYPE        *,
                                         LHS_CATEGORY,
                                         const RHS_TYPE&,
                                         RHS_CATEGORY)
{
    enum { k_FAILURE = -6 };

    return k_FAILURE;
}

template <class LHS_TYPE, class RHS_TYPE>
inline
int bdlat_SymbolicConverter_Imp::resolveDynamicTypes(
                                         LHS_TYPE                         *lhs,
                                         bdlat_TypeCategory::DynamicType,
                                         const RHS_TYPE&                   rhs,
                                         bdlat_TypeCategory::DynamicType)
{
    bdlat_SymbolicConverter_Imp_resolveDynamicRhsProxy<
                        LHS_TYPE,
                        bdlat_TypeCategory::DynamicType> proxy = { this, lhs };

    return bdlat_TypeCategoryUtil::accessByCategory(rhs, proxy);
}

template <class LHS_TYPE, class RHS_TYPE, class RHS_CATEGORY>
inline
int bdlat_SymbolicConverter_Imp::resolveDynamicTypes(
                                          LHS_TYPE                        *lhs,
                                          bdlat_TypeCategory::DynamicType,
                                          const RHS_TYPE&                  rhs,
                                          RHS_CATEGORY)
{
    bdlat_SymbolicConverter_Imp_resolveDynamicLhsProxy<
                                          RHS_TYPE,
                                          RHS_CATEGORY> proxy = { this, &rhs };

    return bdlat_TypeCategoryUtil::manipulateByCategory(lhs, proxy);
}

template <class LHS_TYPE, class LHS_CATEGORY, class RHS_TYPE>
inline
int bdlat_SymbolicConverter_Imp::resolveDynamicTypes(
                                         LHS_TYPE                         *lhs,
                                         LHS_CATEGORY,
                                         const RHS_TYPE&                   rhs,
                                         bdlat_TypeCategory::DynamicType)
{
    bdlat_SymbolicConverter_Imp_resolveDynamicRhsProxy<
                                           LHS_TYPE,
                                           LHS_CATEGORY> proxy = { this, lhs };

    return bdlat_TypeCategoryUtil::accessByCategory(rhs, proxy);
}

template <class LHS_TYPE,
          class LHS_CATEGORY,
          class RHS_TYPE,
          class RHS_CATEGORY>
inline
int bdlat_SymbolicConverter_Imp::resolveDynamicTypes(
                                                  LHS_TYPE        *lhs,
                                                  LHS_CATEGORY     lhsCategory,
                                                  const RHS_TYPE&  rhs,
                                                  RHS_CATEGORY     rhsCategory)
{
    return convert(lhs, lhsCategory, rhs, rhsCategory);
}

// CREATORS

inline
bdlat_SymbolicConverter_Imp::bdlat_SymbolicConverter_Imp(
                                                     bsl::ostream *errorStream)
: d_errorStream_p(errorStream)
{
    BSLS_ASSERT(d_errorStream_p);
}

// MANIPULATORS

template <class LHS_TYPE, class RHS_TYPE>
inline
int bdlat_SymbolicConverter_Imp::convert(LHS_TYPE        *lhs,
                                         const RHS_TYPE&  rhs)
{
    typedef typename bdlat_TypeCategory::Select<LHS_TYPE>::Type LhsCategory;
    typedef typename bdlat_TypeCategory::Select<RHS_TYPE>::Type RhsCategory;

    return resolveDynamicTypes(lhs, LhsCategory(), rhs, RhsCategory());
}

inline
bsl::ostream& bdlat_SymbolicConverter_Imp::errorStream()
{
    return *d_errorStream_p;
}

           // -----------------------------------------------------
           // class bdlat_SymbolicConverter_StoreValue<LVALUE_TYPE>
           // -----------------------------------------------------

// CREATORS

template <class LVALUE_TYPE>
inline
bdlat_SymbolicConverter_StoreValue<LVALUE_TYPE>::
bdlat_SymbolicConverter_StoreValue(LVALUE_TYPE                 *destination,
                                   bdlat_SymbolicConverter_Imp *imp)
: d_destination_p(destination)
, d_imp_p(imp)
{
}

// ACCESSORS

template <class LVALUE_TYPE>
template <class RVALUE_TYPE, class INFO_TYPE>
inline
int bdlat_SymbolicConverter_StoreValue<LVALUE_TYPE>::operator()(
                                                     const RVALUE_TYPE& object,
                                                     const INFO_TYPE&) const
{
    return d_imp_p->convert(d_destination_p, object);
}

template <class LVALUE_TYPE>
template <class RVALUE_TYPE>
inline
int bdlat_SymbolicConverter_StoreValue<LVALUE_TYPE>::operator()(
                                               const RVALUE_TYPE& object) const
{
    return d_imp_p->convert(d_destination_p, object);
}

            // ----------------------------------------------------
            // class bdlat_SymbolicConverter_LoadValue<RVALUE_TYPE>
            // ----------------------------------------------------

// CREATORS

template <class RVALUE_TYPE>
inline
bdlat_SymbolicConverter_LoadValue<RVALUE_TYPE>::
                    bdlat_SymbolicConverter_LoadValue(
                                            const RVALUE_TYPE&           value,
                                            bdlat_SymbolicConverter_Imp *imp)
: d_imp_p(imp)
, d_value(value)
{
}

// ACCESSORS

template <class RVALUE_TYPE>
template <class LVALUE_TYPE, class INFO_TYPE>
inline
int bdlat_SymbolicConverter_LoadValue<RVALUE_TYPE>::operator()(
                                                        LVALUE_TYPE *object,
                                                        const INFO_TYPE&) const
{
    return d_imp_p->convert(object, d_value);
}

template <class RVALUE_TYPE>
template <class LVALUE_TYPE>
inline
int bdlat_SymbolicConverter_LoadValue<RVALUE_TYPE>::operator()(
                                                     LVALUE_TYPE *object) const
{
    return d_imp_p->convert(object, d_value);
}

        // ------------------------------------------------------------
        // class bdlat_SymbolicConverter_StoreInSequence<SEQUENCE_TYPE>
        // ------------------------------------------------------------

// CREATORS

template <class SEQUENCE_TYPE>
inline
bdlat_SymbolicConverter_StoreInSequence<SEQUENCE_TYPE>::
bdlat_SymbolicConverter_StoreInSequence(
                                      SEQUENCE_TYPE               *destination,
                                      bdlat_SymbolicConverter_Imp *imp)
: d_destination_p(destination)
, d_imp_p(imp)
{
}

// ACCESSORS

template <class SEQUENCE_TYPE>
template <class SOURCE_MEMBER_TYPE, class INFO_TYPE>
inline
int bdlat_SymbolicConverter_StoreInSequence<SEQUENCE_TYPE>::operator()(
                                        const SOURCE_MEMBER_TYPE& sourceMember,
                                        const INFO_TYPE&          info) const
{
    enum { k_SUCCESS = 0, k_FAILURE = -7 };

    bdlat_SymbolicConverter_LoadValue<SOURCE_MEMBER_TYPE> loadSourceValue(
                                                                  sourceMember,
                                                                  d_imp_p);

    if (0 != bdlat_SequenceFunctions::manipulateAttribute(d_destination_p,
                                                          loadSourceValue,
                                                          info.name(),
                                                          info.nameLength())) {
        d_imp_p->errorStream()
                 << "Failed to convert attribute '"
                 << bslstl::StringRef(info.name(), info.nameLength()) << "'\n";

        return k_FAILURE;                                             // RETURN
    }

    return k_SUCCESS;
}

          // --------------------------------------------------------
          // class bdlat_SymbolicConverter_StoreInChoice<CHOICE_TYPE>
          // --------------------------------------------------------

// CREATORS

template <class CHOICE_TYPE>
inline
bdlat_SymbolicConverter_StoreInChoice<CHOICE_TYPE>::
bdlat_SymbolicConverter_StoreInChoice(CHOICE_TYPE                 *destination,
                                      bdlat_SymbolicConverter_Imp *imp)
: d_destination_p(destination)
, d_imp_p(imp)
{
}

// ACCESSORS

template <class CHOICE_TYPE>
template <class SOURCE_MEMBER_TYPE, class INFO_TYPE>
int bdlat_SymbolicConverter_StoreInChoice<CHOICE_TYPE>::operator()(
                                        const SOURCE_MEMBER_TYPE& sourceMember,
                                        const INFO_TYPE&          info) const
{
    enum { k_SUCCESS = 0, k_FAILURE = -8 };

    // Make the selection.

    if (0 != bdlat_ChoiceFunctions::makeSelection(d_destination_p,
                                                  info.name(),
                                                  info.nameLength())) {
        d_imp_p->errorStream()
                 << "Failed to make selection '"
                 << bslstl::StringRef(info.name(), info.nameLength()) << "'\n";

        return k_FAILURE;                                             // RETURN
    }

    // Assign the value.

    bdlat_SymbolicConverter_LoadValue<SOURCE_MEMBER_TYPE> loadSourceValue(
                                                                  sourceMember,
                                                                  d_imp_p);

    if (0 != bdlat_ChoiceFunctions::manipulateSelection(d_destination_p,
                                                        loadSourceValue)) {
        d_imp_p->errorStream()
                 << "Failed to convert selection '"
                 << bslstl::StringRef(info.name(), info.nameLength()) << "'\n";

        return k_FAILURE;                                             // RETURN
    }

    return k_SUCCESS;
}

       // -------------------------------------------------------------
       // class bdlat_SymbolicConverter_StoreInArrayElement<ARRAY_TYPE>
       // -------------------------------------------------------------

// CREATORS

template <class ARRAY_TYPE>
inline
bdlat_SymbolicConverter_StoreInArrayElement<ARRAY_TYPE>::
bdlat_SymbolicConverter_StoreInArrayElement(ARRAY_TYPE                  *array,
                                            int                          index,
                                            bdlat_SymbolicConverter_Imp *imp)
: d_array_p(array)
, d_imp_p(imp)
, d_index(index)
{
}

// ACCESSORS

template <class ARRAY_TYPE>
template <class SOURCE_ELEMENT_TYPE>
inline
int bdlat_SymbolicConverter_StoreInArrayElement<ARRAY_TYPE>::operator()(
                                const SOURCE_ELEMENT_TYPE& sourceElement) const
{
    bdlat_SymbolicConverter_LoadValue<SOURCE_ELEMENT_TYPE> loadSourceValue(
                                                                 sourceElement,
                                                                 d_imp_p);

    return bdlat_ArrayFunctions::manipulateElement(d_array_p,
                                                   loadSourceValue,
                                                   d_index);
}

        // ------------------------------------------------------------
        // class bdlat_SymbolicConverter_StoreInNullable<NULLABLE_TYPE>
        // ------------------------------------------------------------

// CREATORS

template <class NULLABLE_TYPE>
inline
bdlat_SymbolicConverter_StoreInNullable<NULLABLE_TYPE>::
bdlat_SymbolicConverter_StoreInNullable(
                                      NULLABLE_TYPE               *destination,
                                      bdlat_SymbolicConverter_Imp *imp)
: d_destination_p(destination)
, d_imp_p(imp)
{
}

// ACCESSORS

template <class NULLABLE_TYPE>
template <class VALUE_TYPE>
inline
int bdlat_SymbolicConverter_StoreInNullable<NULLABLE_TYPE>::operator()(
                                                 const VALUE_TYPE& value) const
{
    bdlat_NullableValueFunctions::makeValue(d_destination_p);

    // Assign the value.

    bdlat_SymbolicConverter_LoadValue<VALUE_TYPE> loadSourceValue(value,
                                                                  d_imp_p);

    return bdlat_NullableValueFunctions::manipulateValue(d_destination_p,
                                                         loadSourceValue);
}

                       // ------------------------------
                       // struct bdlat_SymbolicConverter
                       // ------------------------------

template <class LHS_TYPE, class RHS_TYPE>
inline
int bdlat_SymbolicConverter::convert(LHS_TYPE        *lhs,
                                     const RHS_TYPE&  rhs)
{
    bsl::ostream nullStream(0);
    return bdlat_SymbolicConverter::convert(lhs, rhs, nullStream);
}

template <class LHS_TYPE, class RHS_TYPE>
inline
int bdlat_SymbolicConverter::convert(LHS_TYPE        *lhs,
                                     const RHS_TYPE&  rhs,
                                     bsl::ostream&    errorStream)
{
    bdlat_ValueTypeFunctions::reset(lhs);

    bdlat_SymbolicConverter_Imp imp(&errorStream);

    return imp.convert(lhs, rhs);
}

}  // close enterprise namespace

#endif

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