// balcl_optionvalue.h                                                -*-C++-*-
#ifndef INCLUDED_BALCL_OPTIONVALUE
#define INCLUDED_BALCL_OPTIONVALUE

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

//@PURPOSE: Provide a variant type for command-line-option values.
//
//@CLASSES:
//  balcl::OptionValue: the value of a user supplied command-line option
//
//@SEE_ALSO: balcl_optiontype, balcl_commandline
//
//@DESCRIPTION: This component provides a value-semantic class,
// 'balcl::OptionValue', that can have a value of any of the types specified by
// 'balcl::OptionType' -- i.e., any of the values that can be associated with a
// command-line option by 'balcl::CommandLine'.  The 'balcl::OptionValue' class
// has two related states:
//
//: 1 A default-constructed 'balcl::OptionValue' object is in the "unset state"
//:   -- meaning that no type has been defined for a value (its type is
//:   'void').  In this state, 'balcl::OptionType::e_VOID == type()' and
//:   'false == hasNonVoidType()'.  To have a value, a type must be specified
//:   for the value by using the 'setType' method or using one of the
//:   constructors that define an initial value.  {Example 1} shows how this
//:   state can be set and reset.
//
//: 2 If a 'balcl::OptionValue' object has a (non-'void') type it can have a
//:   value of that type or be in a "null state".  Objects in the null state
//:   can be used to represent the value of command-line options that were not
//:   entered on the command line (assuming no default value was specified for
//:   the option).  {Example 2} shows how this feature can be used.
//
///Usage
///-----
// This section illustrates intended use of this component.
//
///Example 1: Basic Use of 'balcl::OptionValue'
/// - - - - - - - - - - - - - - - - - - - - - -
// The following snippets of code illustrate how to create and use a
// 'balcl::OptionValue' object.  Note that 'balcl::OptionValue' objects are
// typically used in a description of a sequence of command-line options (see
// {'balcl_optiontype'}).
//
// First, we create a default 'balcl::OptionValue', 'valueA', and observe that
// it is in the unset state:
//..
//  balcl::OptionValue valueA;
//
//  assert(false                     == valueA.hasNonVoidType());
//  assert(balcl::OptionType::e_VOID == valueA.type());
//..
// Next, we create a second 'balcl::OptionValue' having the value 5, and then
// confirm its value and observe that it does not compare equal to the
// 'valueA':
//..
//  balcl::OptionValue valueB(5);
//
//  assert(true                     == valueB.hasNonVoidType());
//  assert(balcl::OptionType::e_INT == valueB.type());
//  assert(5                        == valueB.the<int>());
//
//  assert(valueA != valueB);
//..
// Then, we call the 'reset' method of 'valueB' resetting it to the unset
// state, and observe that 'valueA' now compares equal to 'valueB':
//..
//  valueB.reset();
//
//  assert(valueA == valueB);
//..
// Now, we change the type of 'valueA' so that it can be hold a 'double' value:
//..
//  valueA.setType(balcl::OptionType::e_DOUBLE);
//  assert(true                        == valueA.hasNonVoidType());
//  assert(balcl::OptionType::e_DOUBLE == valueA.type());
//  assert(double()                    == valueA.the<double>());
//
//  valueA.set(6.0);
//  assert(6.0                         == valueA.the<double>());
//..
// Finally, we set the object to the null state.  Notice that the type of that
// value is not changed:
//..
//  valueA.setNull();
//  assert(true                        == valueA.isNull());
//  assert(balcl::OptionType::e_DOUBLE == valueA.type());
//..
//
///Example 2: Interpreting Option Parser Results
///- - - - - - - - - - - - - - - - - - - - - - -
// Command-line options have values of many different types (e.g., 'int',
// 'double', string, date) or their values may not be specified -- after all,
// some command-line options may be *optional*.  The 'balcl::OptionValue' class
// can be used to represent such values.
//
// First, we define 'MyCommandLineParser', a simple command-line argument
// parser.  This class accepts a description (e.g., option name, value type) of
// allowable options on construction and provides a 'parse' method that accepts
// 'argc' and 'argv', the values made available (by the operating system) to
// 'main':
//..
//                      // =========================
//                      // class MyCommandLineParser
//                      // =========================
//
//  class MyCommandLineParser {
//      // ...
//
//    public:
//      // CREATORS
//      MyCommandLineParser(const MyOptionDescription *descriptions,
//                          bsl::size_t                count);
//          // Create an object that can parse command-line arguments that
//          // satisfy the specified 'descriptions', an array containing the
//          // specified 'count' elements.
//
//      // ...
//
//      // MANIPULATORS
//      int parse(int argc, const char **argv);
//          // Parse the command-line options in the specified 'argv', an array
//          // having the specified 'argc' elements.  Return 0 on success --
//          // i.e., the options were compatible with the option descriptions
//          // specified on construction -- and a non-zero value otherwise.
//
//      // ...
//..
// After a successful call to the 'parse' method, the results are available by
// several accessors.  Note that the 'index' of a result corresponds to the
// index of that option in the description provided on construction:
//..
//      // ACCESSORS
//      bool isParsed() const;
//          // Return 'true' if the most recent call to 'parsed' was successful
//          // and 'false' otherwise.
//
//      const char *name (bsl::size_t index) const;
//          // Return of the name of the parsed option at the specified 'index'
//          // position.  The behavior is undefined unless
//          // '0 <= index < numOptions()' and 'true == isParsed()'
//
//      const balcl::OptionValue& value(bsl::size_t index) const;
//          // Return a 'const' reference to the value (possibly in a null
//          // state) of the parsed option at the specified 'index' position.
//          // The behavior is undefined unless '0 <= index < numOptions()' and
//          // 'true == isParsed()'.
//
//      bsl::size_t numOptions() const;
//          // Return the number of parsed options.  The behavior is undefined
//          // unless 'true == isParsed()'.
//
//      // ...
//  };
//..
// Note that neither our option description nor our parser support the concept
// of default values for options that are not entered on the command line.
//
// Then, we create a description having three allowable options (elided), a
// parser object, and invoke 'parse' on the arguments available from 'main':
//..
//  int main(int argc, const char **argv)
//  {
//      MyOptionDescription optionDescriptions[NUM_OPTIONS] = {
//          // ...
//      };
//
//      MyCommandLineParser parser(optionDescriptions, NUM_OPTIONS);
//
//      int rc = parser.parse(argc, argv);
//      assert(0    == rc);
//      assert(true == parser.isParsed());
//..
// Now, we examine the value of each defined option:
//..
//      for (bsl::size_t i = 0; i < parser.numOptions(); ++i) {
//          const char                *name  = parser.name(i);
//          const balcl::OptionValue&  value = parser.value(i);
//..
// Since our (toy) parser has no feature for handling default values for
// options that are not specified on the command line, we must handle those
// explicitly.
//
// If the option named "outputDir" was set, we use that value; otherwise, we
// set a default value, the current directory:
//..
//          if (0 == bsl::strcmp("outputDir", name)) {
//              setOutputDir(value.isNull()
//                           ? "."
//                           : value.the<bsl::string>().c_str());
//          }
//..
// If the option named "verbosityLevel" was set we use that value; otherwise,
// we set a default value, '1':
//..
//          if (0 == bsl::strcmp("verbosityLevel", name)) {
//              setVerbosityLevel(value.isNull()
//                                ? 1
//                                : value.the<int>());
//          }
//..
// The option named "caseInsensitive" has no associated value.  If that option
// appeared on the command line, the value of the program flag is set to
// 'true', otherwise ('false == isNull()') that flag is set to 'false':
//..
//          if (0 == bsl::strcmp("caseInsensitive", name)) {
//              setCaseInsensitivityFlag(value.isNull()
//                                       ? false
//                                       : true);
//          }
//      }
//..
// Finally, we continue with the execution of our program using the values
// obtained from the command-line options:
//..
//      // ...
//
//      return 0;
//  }
//..

#include <balscm_version.h>

#include <balcl_optiontype.h>

#include <bdlt_date.h>
#include <bdlt_datetime.h>
#include <bdlt_time.h>

#include <bdlb_printmethods.h> // 'bdlb::HasPrintMethod', 'bdlb::PrintMethods'
#include <bdlb_variant.h>

#include <bslma_allocator.h>
#include <bslma_usesbslmaallocator.h>

#include <bslmf_isbitwisemoveable.h>
#include <bslmf_isbitwiseequalitycomparable.h>
#include <bslmf_nestedtraitdeclaration.h>
#include <bslmf_nil.h>

#include <bsls_assert.h>
#include <bsls_types.h>  // 'bsls::Types::Int64'

#include <bsl_iosfwd.h>  // 'bsl::ostream'
#include <bsl_string.h>
#include <bsl_vector.h>

namespace BloombergLP {

namespace balcl {

                        // ========================
                        // class OptionValue_NullOf
                        // ========================

class OptionValue_NullOf {
    // This single-attribute class represents a null value of a given nullable
    // 'balcl::OptionType'.  'OptionValue' uses this type to represent its
    // state where there is a known type, but no value for it.  Note that
    // 'OptionType::e_VOID' is *not* nullable, therefore not supported here.
    // Note that: There is no 'swap' member or namespace-level function
    // declared (and defined) for this class on purpose, the general swap works
    // fast for such a simple type.

    // DATA
    OptionType::Enum d_type;

  public:
    // TRAITS
    BSLMF_NESTED_TRAIT_DECLARATION(OptionValue_NullOf,
                                   bslmf::IsBitwiseMoveable);
    BSLMF_NESTED_TRAIT_DECLARATION(OptionValue_NullOf,
                                   bslmf::IsBitwiseEqualityComparable);
    BSLMF_NESTED_TRAIT_DECLARATION(OptionValue_NullOf,
                                   bdlb::HasPrintMethod);

    // CREATORS
    explicit OptionValue_NullOf(OptionType::Enum optionType);
        // Create an 'OptionValue_NullOf' object with the specified nullable
        // 'optionType'.  The behavior is undefined if
        // 'optionType == OptionType::e_VOID'.

    // ACCESSORS
    template <class TYPE>
    bool isType(const TYPE&) const;
        // Return 'true' if 'TYPE' corresponds to the 'type' attribute, and
        // 'false' if it does not.

    OptionType::Enum type() const;
        // Return the option 'type' of this object.

                                  // Aspects

    bsl::ostream& print(bsl::ostream& stream,
                        int           level          = 0,
                        int           spacesPerLevel = 4) const;
        // Format this object to the specified output 'stream' at the (absolute
        // value of) the optionally specified indentation 'level' and return a
        // reference to 'stream'.  If 'level' is specified, optionally specify
        // 'spacesPerLevel', the number of spaces per indentation level for
        // this and all of its nested objects.  If 'level' is negative,
        // suppress indentation of the first line.  If 'spacesPerLevel' is
        // negative, format the entire output on one line, suppressing all but
        // the initial indentation (as governed by 'level').  If 'stream' is
        // not valid on entry, this operation has no effect.
};

// FREE OPERATORS
bool operator==(const OptionValue_NullOf& lhs, const OptionValue_NullOf& rhs);
    // Return 'true' if the 'type' of the specified 'lhs' and 'rhs' are equal,
    // and return 'false' if they are not equal.  Two 'OptionValue_NullOf'
    // objects are equal when their 'type' attributes are equal.

bool operator!=(const OptionValue_NullOf& lhs, const OptionValue_NullOf& rhs);
    // Return 'true' if the 'type' of the specified 'lhs' and 'rhs' are not
    // equal, and return 'false' if they are not equal.  Two
    // 'OptionValue_NullOf' objects are equal when their 'type' attributes are
    // note equal.

bsl::ostream& operator<<(bsl::ostream&             stream,
                         const OptionValue_NullOf& object);
    // Write the value of the specified 'object' to the specified output
    // 'stream' in a single-line format, and return a reference to 'stream'.
    // If 'stream' is not valid on entry, this operation has no effect.  Note
    // that this human-readable format is not fully specified, can change
    // without notice, and is logically equivalent to:
    // 'object.print(stream, 0, -1);'

                        // =================
                        // class OptionValue
                        // =================
class OptionValue {
    // This class implements a special-use value-semantic variant type used to
    // represent values parsed from process command lines.  Accordingly, this
    // class can represent values of any of the types defined in
    // {'balcl_optiontype'}.  Furthermore, that value can also be in a null
    // state (defined type but no defined value) to represent allowed options
    // that do not appear among the command-line arguments (and for which no
    // default value has been configured).

  private:
    // PRIVATE TYPES
    typedef OptionType::Bool          Bool;
    typedef OptionType::Char          Char;
    typedef OptionType::Int           Int;
    typedef OptionType::Int64         Int64;
    typedef OptionType::Double        Double;
    typedef OptionType::String        String;
    typedef OptionType::Datetime      Datetime;
    typedef OptionType::Date          Date;
    typedef OptionType::Time          Time;
    typedef OptionType::CharArray     CharArray;
    typedef OptionType::IntArray      IntArray;
    typedef OptionType::Int64Array    Int64Array;
    typedef OptionType::DoubleArray   DoubleArray;
    typedef OptionType::StringArray   StringArray;
    typedef OptionType::DatetimeArray DatetimeArray;
    typedef OptionType::DateArray     DateArray;
    typedef OptionType::TimeArray     TimeArray;

    typedef bdlb::Variant<Bool,
                          Char,
                          Int,
                          Int64,
                          Double,
                          String,
                          Datetime,
                          Date,
                          Time,      // DO NOT change the order of these types!
                          CharArray,
                          IntArray,
                          Int64Array,
                          DoubleArray,
                          StringArray,
                          DatetimeArray,
                          DateArray,
                          TimeArray,
                          OptionValue_NullOf> ValueVariant;

    // DATA
    ValueVariant d_value;  // the object's value

    // FRIENDS
    friend bool operator==(const OptionValue&, const OptionValue&);
    friend void swap(OptionValue&, OptionValue&);

    // PRIVATE MANIPULATORS
    void init(OptionType::Enum type);
        // Set the type of the contained variant object to have the specified
        // 'type' and have the default value for 'type'.  The behavior is
        // undefined unless 'OptionType::e_VOID != type()'.

  public:
    // TRAITS
    BSLMF_NESTED_TRAIT_DECLARATION(OptionValue, bslma::UsesBslmaAllocator);
    BSLMF_NESTED_TRAIT_DECLARATION(OptionValue, bdlb::HasPrintMethod);

    // CREATORS
    OptionValue();
    explicit
    OptionValue(bslma::Allocator *basicAllocator);
        // Create a command-line-option value object in the unset state (i.e.,
        // 'OptionType::e_VOID == type()').  Optionally specify a
        // 'basicAllocator' used to supply memory.  If 'basicAllocator' is not
        // specified, the currently installed default allocator is used.  Note
        // that (atypically) this constructor disallows 0 for 'basicAllocator';
        // invoking the constructor with 0 dispatches to the constructor
        // overload that explicitly accepts an 'int' value for its first
        // argument (see below).

    OptionValue(OptionType::Enum  type,
                bslma::Allocator *basicAllocator = 0); // IMPLICIT
        // Create a command-line-option value object having the type
        // corresponding to the specified 'type' and, if
        // 'OptionType::e_VOID != type', having the default value of that type.
        // Optionally specify a 'basicAllocator' used to supply memory.  If
        // 'basicAllocator' is 0, the currently installed default allocator is
        // used.

    explicit
    OptionValue(bool                value,
                bslma::Allocator   *basicAllocator = 0);
    explicit
    OptionValue(char                value,
                bslma::Allocator   *basicAllocator = 0);
    explicit
    OptionValue(int                 value,
                bslma::Allocator   *basicAllocator = 0);
    explicit
    OptionValue(bsls::Types::Int64  value,
                bslma::Allocator   *basicAllocator = 0);
    explicit
    OptionValue(double              value,
                bslma::Allocator   *basicAllocator = 0);
    explicit
    OptionValue(const bsl::string&  value,
                bslma::Allocator   *basicAllocator = 0);
    explicit
    OptionValue(bdlt::Datetime      value,
                bslma::Allocator   *basicAllocator = 0);
    explicit
    OptionValue(bdlt::Date          value,
                bslma::Allocator   *basicAllocator = 0);
    explicit
    OptionValue(bdlt::Time          value,
                bslma::Allocator   *basicAllocator = 0);
    explicit
    OptionValue(const bsl::vector<char>&                value,
                bslma::Allocator                       *basicAllocator = 0);
    explicit
    OptionValue(const bsl::vector<int>&                 value,
                bslma::Allocator                       *basicAllocator = 0);
    explicit
    OptionValue(const bsl::vector<bsls::Types::Int64>&  value,
                bslma::Allocator                       *basicAllocator = 0);
    explicit
    OptionValue(const bsl::vector<double>&              value,
                bslma::Allocator                       *basicAllocator = 0);
    explicit
    OptionValue(const bsl::vector<bsl::string>&         value,
                bslma::Allocator                       *basicAllocator = 0);
    explicit
    OptionValue(const bsl::vector<bdlt::Datetime>&      value,
                bslma::Allocator                       *basicAllocator = 0);
    explicit
    OptionValue(const bsl::vector<bdlt::Date>&          value,
                bslma::Allocator                       *basicAllocator = 0);
    explicit
    OptionValue(const bsl::vector<bdlt::Time>&          value,
                bslma::Allocator                       *basicAllocator = 0);
        // Create a command-line-option value object having the type and value
        // of the specified 'value'.  Optionally specify a 'basicAllocator'
        // used to supply memory.  If 'basicAllocator' is 0, the currently
        // installed default allocator is used.

    OptionValue(const OptionValue&  original,
                bslma::Allocator   *basicAllocator = 0);
        // Create a 'OptionValue' object having the same value as the specified
        // 'original' object.  Optionally specify a 'basicAllocator' used to
        // supply memory.  If 'basicAllocator' is 0, the currently installed
        // default allocator is used.

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

    // MANIPULATORS
    OptionValue& operator=(const OptionValue& rhs);
        // Assign to this object the value of the specified 'rhs' object, and
        // return a reference providing modifiable access to this object.

    void reset();
        // Reset this object to its default constructed (unset) state.  The
        // existing value, if any, is destroyed.  Note that on return
        // 'OptionType::e_VOID == type()'.

    template <class TYPE>
    void set(const TYPE& value);
        // Set the value of this object to the specified 'value'.  The behavior
        // is undefined unless 'OptionType::TypeToEnum<TYPE>::value == type()'
        // for the (template parameter) 'TYPE' and
        // 'OptionType::e_VOID != type()'.

    void setNull();
        // Set the value of this object, irrespective of that value's type, to
        // its null state.  The behavior is undefined unless
        // 'true == hasNonVoidType()'.  Note that 'type()' is not changed.

    void setType(OptionType::Enum type);
        // Set the type of this object to the specified 'type' and the value to
        // the default value of that type.

    template <class TYPE>
    TYPE& the();
        // Return a reference providing modifiable access to the underlying
        // variant object of this command-line-option value object.  The
        // behavior is undefined unless 'OptionType::e_VOID != type()',
        // 'OptionType::TypeToEnum<TYPE>::value == type()', and
        // 'false == isNull()'.

                                  // Aspects

    void swap(OptionValue& other);
        // Efficiently exchange the value of this object with the value of the
        // specified 'other' object.  This method provides the no-throw
        // exception-safety guarantee if either 'type()' is the same as
        // 'other.type()', or neither 'type()' nor 'other.type()' is a type
        // that requires allocation; otherwise, it provides the basic
        // guarantee.  The behavior is undefined unless this object was created
        // with the same allocator as 'other'.

    // ACCESSORS
    bool hasNonVoidType() const;
        // Return 'true' if this object is in the unset state, and 'false'
        // otherwise.  Note that if 'false == hasNonVoidType()' then
        // 'OptionType::e_VOID == type()'.

    bool isNull() const;
        // Return 'true' if the value of this object (irrespective of
        // non-'void' type) is null.  The behavior is undefined unless
        // 'true == hasNonVoidType()'.

    template <class TYPE>
    const TYPE& the() const;
        // Return a 'const' reference to the value of this command line option.
        // The behavior is undefined unless 'OptionType::e_VOID != type()',
        // 'OptionType::typeToEnum<TYPE>::value == type()', and
        // 'false == isNull()'.

    OptionType::Enum type() const;
        // Return the type of this command-line-option value.  The type
        // 'OptionType::e_VOID' represents the unset state.

                                  // Aspects

    bslma::Allocator *allocator() const;
        // Return the allocator used by this object to supply memory.  Note
        // that if no allocator was supplied at construction the currently
        // installed default allocator is used.

    bsl::ostream& print(bsl::ostream& stream,
                        int           level = 0,
                        int           spacesPerLevel = 4) const;
        // Write the value of this object to the specified output 'stream' in a
        // human-readable format, and return a reference to 'stream'.
        // Optionally specify an initial indentation 'level', whose absolute
        // value is incremented recursively for nested objects.  If 'level' is
        // specified, optionally specify 'spacesPerLevel', whose absolute value
        // indicates the number of spaces per indentation level for this and
        // all of its nested objects.  If 'level' is negative, suppress
        // indentation of the first line.  If 'spacesPerLevel' is negative,
        // format the entire output on one line, suppressing all but the
        // initial indentation (as governed by 'level').  If 'stream' is not
        // valid on entry, this operation has no effect.  Note that the format
        // is not fully specified, and can change without notice.
};

// FREE OPERATORS
bool operator==(const OptionValue& lhs, const OptionValue& rhs);
    // Return 'true' if the specified 'lhs' and 'rhs' objects have the same
    // value, and 'false' otherwise.  Two 'OptionValue' objects have the same
    // value if they have the same type, and (if the type is not 'e_VOID') the
    // value of that type (as accessed through 'the*' methods) is the same.

bool operator!=(const OptionValue& lhs, const OptionValue& rhs);
    // Return 'true' if the specified 'lhs' and 'rhs' objects do not have the
    // same value, and 'false' otherwise.  Two 'OptionValue' objects do not
    // have the same value if their type is not the same, or (if their type is
    // not 'e_VOID') the value of that type (as accessed through 'the*'
    // methods) is not the same.

bsl::ostream& operator<<(bsl::ostream& stream, const OptionValue& object);
    // Write the value of the specified 'object' to the specified output
    // 'stream' in a single-line format, and return a reference to 'stream'.
    // If 'stream' is not valid on entry, this operation has no effect.  Note
    // that this human-readable format is not fully specified, can change
    // without notice, and is logically equivalent to:
    //..
    //  print(stream, 0, -1);
    //..

// FREE FUNCTIONS
void swap(OptionValue& a, OptionValue& b);
    // Swap the value of the specified 'a' object with the value of the
    // specified 'b' object.  This method provides the no-throw
    // exception-safety guarantee if either 'a.type()' is the same as
    // 'b.type()' and 'a' and 'b' were created with the same allocator, or
    // neither 'a.type()' nor 'b.type()' is a type that requires allocation;
    // otherwise, it provides the basic guarantee.

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

                        // -----------------
                        // class OptionValue
                        // -----------------

// CREATORS
inline
OptionValue::OptionValue()
: d_value()
{
}

inline
OptionValue::OptionValue(bslma::Allocator *basicAllocator)
: d_value(basicAllocator)
{
}

inline
OptionValue::OptionValue(OptionType::Enum  type,
                         bslma::Allocator *basicAllocator)
: d_value(basicAllocator)
{
    init(type);
}

inline
OptionValue::OptionValue(bool              value,
                         bslma::Allocator *basicAllocator)
: d_value(value, basicAllocator)
{
}

inline
OptionValue::OptionValue(char              value,
                         bslma::Allocator *basicAllocator)
: d_value(value, basicAllocator)
{
}

inline
OptionValue::OptionValue(int               value,
                         bslma::Allocator *basicAllocator)
: d_value(value, basicAllocator)
{
}

inline
OptionValue::OptionValue(bsls::Types::Int64  value,
                         bslma::Allocator   *basicAllocator)
: d_value(value, basicAllocator)
{
}

inline
OptionValue::OptionValue(double            value,
                         bslma::Allocator *basicAllocator)
: d_value(value, basicAllocator)
{
}

inline
OptionValue::OptionValue(const bsl::string&  value,
                         bslma::Allocator   *basicAllocator)
: d_value(value, basicAllocator)
{
    // TBD: This now could be changed to 'bsl::string_view' IFF it is a
    // compatible change for users.
    //
    // Implementation note: Changing 'bsl::string' to a string view would not
    // be a constructive change, because 'bdlb::NullableValue<bsl::string>' is
    // not constructible from a string view.  Note that 'value', as a
    // 'bsl::string', is constructible from 'const char *', an 'std::string',
    // or an 'std::pmr::string'.
}

inline
OptionValue::OptionValue(bdlt::Datetime    value,
                         bslma::Allocator *basicAllocator)
: d_value(value, basicAllocator)
{
}

inline
OptionValue::OptionValue(bdlt::Date        value,
                         bslma::Allocator *basicAllocator)
: d_value(value, basicAllocator)
{
}

inline
OptionValue::OptionValue(bdlt::Time        value,
                         bslma::Allocator *basicAllocator)
: d_value(value, basicAllocator)
{
}

inline
OptionValue::OptionValue(const bsl::vector<char>&  value,
                         bslma::Allocator         *basicAllocator)
: d_value(value, basicAllocator)
{
}

inline
OptionValue::OptionValue(const bsl::vector<int>&  value,
                         bslma::Allocator        *basicAllocator)
: d_value(value, basicAllocator)
{
}

inline
OptionValue::OptionValue(
                        const bsl::vector<bsls::Types::Int64>&  value,
                        bslma::Allocator                       *basicAllocator)
: d_value(value, basicAllocator)
{
}

inline
OptionValue::OptionValue(const bsl::vector<double>&  value,
                         bslma::Allocator           *basicAllocator)
: d_value(value, basicAllocator)
{
}

inline
OptionValue::OptionValue(const bsl::vector<bsl::string>&  value,
                         bslma::Allocator                *basicAllocator)
: d_value(value, basicAllocator)
{
}

inline
OptionValue::OptionValue(const bsl::vector<bdlt::Datetime>&  value,
                         bslma::Allocator                   *basicAllocator)
: d_value(value, basicAllocator)
{
}

inline
OptionValue::OptionValue(const bsl::vector<bdlt::Date>&  value,
                         bslma::Allocator               *basicAllocator)
: d_value(value, basicAllocator)
{
}

inline
OptionValue::OptionValue(const bsl::vector<bdlt::Time>&  value,
                         bslma::Allocator               *basicAllocator)
: d_value(value, basicAllocator)
{
}

inline
OptionValue::OptionValue(const OptionValue&  original,
                         bslma::Allocator   *basicAllocator)
: d_value(original.d_value, basicAllocator)
{
}

// MANIPULATORS
inline
OptionValue& OptionValue::operator=(const OptionValue& rhs)
{
    d_value = rhs.d_value;
    return *this;
}

inline
void OptionValue::reset()
{
    d_value.reset();
}

template <class TYPE>
inline
void OptionValue::set(const TYPE& value)
{
    BSLS_ASSERT(d_value.is<TYPE>() ||
                (d_value.is<OptionValue_NullOf>() &&
                 d_value.the<OptionValue_NullOf>().isType(value)));

    d_value.assign(value);
}

inline
void OptionValue::setNull()
{
    BSLS_ASSERT(!d_value.isUnset());

    if (d_value.is<OptionValue_NullOf>()) { // Already null.
        return;                                                       // RETURN
    }

    d_value.createInPlace<OptionValue_NullOf>(this->type());

}

inline
void OptionValue::setType(OptionType::Enum type)
{
    d_value.reset();
    init(type);
}

template <class TYPE>
inline
TYPE& OptionValue::the()
{
    BSLS_ASSERT(d_value.is<TYPE>());

    return d_value.the<TYPE>();
}

                                  // Aspects

inline
void OptionValue::swap(OptionValue& other)
{
    BSLS_ASSERT(allocator() == other.allocator());

    d_value.swap(other.d_value);
}

// ACCESSORS
inline
bool OptionValue::hasNonVoidType() const
{
    return !d_value.isUnset();
}

inline
bool OptionValue::isNull() const
{
    BSLS_ASSERT(!d_value.isUnset());

    return d_value.is<OptionValue_NullOf>();
}

template <class TYPE>
inline
const TYPE& OptionValue::the() const
{
    BSLS_ASSERT(d_value.is<TYPE>());

    return d_value.the<TYPE>();
}

                                  // Aspects
inline
bslma::Allocator *OptionValue::allocator() const
{
    return d_value.getAllocator();
}

}  // close package namespace

// FREE OPERATORS
inline
bool balcl::operator==(const OptionValue& lhs, const OptionValue& rhs)
{
    return lhs.d_value == rhs.d_value;
}

inline
bool balcl::operator!=(const OptionValue& lhs, const OptionValue& rhs)
{
    return !(lhs == rhs);
}

inline
bsl::ostream& balcl::operator<<(bsl::ostream&      stream,
                                const OptionValue& object)
{
    return object.print(stream, 0, -1);
}

// FREE FUNCTIONS
inline
void balcl::swap(OptionValue& a, OptionValue& b)
{
    // 'bdlb::Variant' member 'swap' supports differing allocators.

    a.d_value.swap(b.d_value);
}

namespace balcl {

                        // ------------------------
                        // class OptionValue_NullOf
                        // ------------------------
// CREATORS
inline
OptionValue_NullOf::OptionValue_NullOf(OptionType::Enum optionType)
: d_type(optionType)
{
    BSLS_ASSERT(optionType != OptionType::e_VOID);
}

// ACCESSORS
template <class TYPE>
inline
bool OptionValue_NullOf::isType(const TYPE&) const
{
    return d_type == OptionType::TypeToEnum<TYPE>::value;
}

inline
OptionType::Enum OptionValue_NullOf::type() const
{
    return d_type;
}

                                  // Aspects

inline
bsl::ostream& OptionValue_NullOf::print(bsl::ostream& stream,
                                        int           level,
                                        int           spacesPerLevel) const
{
    return bdlb::PrintMethods::print(stream, "NULL", level, spacesPerLevel);
}

}  // close package namespace

// FREE OPERATORS
inline
bool balcl::operator==(const OptionValue_NullOf& lhs,
                       const OptionValue_NullOf& rhs)
{
    return lhs.type() == rhs.type();
}

inline
bool balcl::operator!=(const OptionValue_NullOf& lhs,
                       const OptionValue_NullOf& rhs)
{
    return !(lhs == rhs);
}

                                  // Aspects

inline
bsl::ostream& balcl::operator<<(bsl::ostream&             stream,
                                const OptionValue_NullOf& object)
{
    return object.print(stream, 0, -1);
}

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