// bdlt_iso8601utilconfiguration.h                                    -*-C++-*-
#ifndef INCLUDED_BDLT_ISO8601UTILCONFIGURATION
#define INCLUDED_BDLT_ISO8601UTILCONFIGURATION

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

//@PURPOSE: Provide an attribute class to configure ISO 8601 string generation.
//
//@CLASSES:
//  bdlt::Iso8601UtilConfiguration: configuration for ISO 8601 strings
//
//@SEE_ALSO: bdlt_iso8601util
//
//@DESCRIPTION: This component provides an unconstrained (value-semantic)
// attribute class, 'bdlt::Iso8601UtilConfiguration', that may be used to
// configure various aspects of generated ISO 8601 strings.
//
///Attributes
///----------
//..
//             Name             Type   Default
//  -------------------------   ----   -------
//  fractionalSecondPrecision   int     3
//  omitColonInZoneDesignator   bool    false
//  useCommaForDecimalSign      bool    false
//  useZAbbreviationForUtc      bool    false
//..
//: o 'fractionalSecondPrecision': number of digits used to represent
//:   fractional seconds; must be in the range '0 .. 6'.
//:
//: o 'omitColonInZoneDesignator': 'true' if ':' should be omitted from zone
//:   designators.
//:
//: o 'useCommaForDecimalSign': 'true' if ',' should be used as the decimal
//:   sign in fractional seconds; otherwise, '.' should be used.
//:
//: o 'useZAbbreviationForUtc': 'true' if 'Z' should be used for the zone
//:   designator instead of '+00:00' (specific to UTC).
//
///Default Configuration
///---------------------
// This component also provides a (process-wide) default configuration that may
// be set and retrieved via the 'setDefaultConfiguration' and
// 'defaultConfiguration' class methods, respectively.  See Usage Example 2 for
// further details.
//
///Usage
///-----
// This section illustrates intended use of this component.
//
///Example 1: Configuring ISO 8601 String Generation
///- - - - - - - - - - - - - - - - - - - - - - - - -
// This example demonstrates creation of a 'bdlt::Iso8601UtilConfiguration'
// object that may be used to influence the format of the output produced by a
// hypothetical utility, 'my::Iso8601Util', that generates and parses ISO 8601
// strings for 'bdlt' vocabulary types (see 'bdlt_iso8601util', which provides
// just such functionality).  In particular, suppose that given a sample
// 'bdlt::TimeTz' object:
//..
//  const bdlt::TimeTz timeTz(bdlt::Time(8, 59, 59, 123), 240);
//..
// 'my::Iso8601Util' produces, by default, the following (valid) ISO 8601
// string:
//..
//  08:59:59.123+04:00
//..
// However, we would like to produce the following (also valid) ISO 8601 string
// instead:
//..
//  08:59:59,123+0400
//..
// 'bdlt::Iso8601UtilConfiguration' can be used to obtain the desired result
// assuming that 'my::Iso8601Util' uses 'bdlt::Iso8601UtilConfiguration' to
// affect the format of generated strings in this fashion (e.g., again see
// 'bdlt_iso8601util').
//
// First, we construct a 'bdlt::Iso8601UtilConfiguration' object that has the
// default value:
//..
//  bdlt::Iso8601UtilConfiguration configuration;
//  assert( configuration.fractionalSecondPrecision() == 3);
//  assert(!configuration.omitColonInZoneDesignator());
//  assert(!configuration.useCommaForDecimalSign());
//  assert(!configuration.useZAbbreviationForUtc());
//..
// Then, we modify 'configuration' to indicate that we want to use ',' as the
// decimal sign (in fractional seconds):
//..
//  configuration.setUseCommaForDecimalSign(true);
//  assert( configuration.fractionalSecondPrecision() == 3);
//  assert(!configuration.omitColonInZoneDesignator());
//  assert( configuration.useCommaForDecimalSign());
//  assert(!configuration.useZAbbreviationForUtc());
//..
// Finally, we modify 'configuration' to indicate that we want to omit the ':'
// in zone designators:
//..
//  configuration.setOmitColonInZoneDesignator(true);
//  assert( configuration.fractionalSecondPrecision() == 3);
//  assert( configuration.omitColonInZoneDesignator());
//  assert( configuration.useCommaForDecimalSign());
//  assert(!configuration.useZAbbreviationForUtc());
//..
// Our 'configuration' object can now be supplied to 'my::Iso8601Util' to
// produce the desired result.
//
///Example 2: Setting the Process-Wide Default Configuration
///- - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// This example demonstrates how to establish the process-wide default
// configuration.
//
// First, we retrieve the default configuration in effect at process start-up
// and note that it has the default-constructed value:
//..
//  bdlt::Iso8601UtilConfiguration configuration =
//                      bdlt::Iso8601UtilConfiguration::defaultConfiguration();
//  assert(bdlt::Iso8601UtilConfiguration() == configuration);
//  assert( configuration.fractionalSecondPrecision() == 3);
//  assert(!configuration.omitColonInZoneDesignator());
//  assert(!configuration.useCommaForDecimalSign());
//  assert(!configuration.useZAbbreviationForUtc());
//..
// Next, we modify 'configuration' to indicate that we want to output 'Z' when
// the zone designator is UTC (i.e., instead of '+00:00'):
//..
//  configuration.setUseZAbbreviationForUtc(true);
//  assert( configuration.fractionalSecondPrecision() == 3);
//  assert(!configuration.omitColonInZoneDesignator());
//  assert(!configuration.useCommaForDecimalSign());
//  assert( configuration.useZAbbreviationForUtc());
//..
// Then, we modify 'configuration' to display milliseconds:
//..
//  configuration.setFractionalSecondPrecision(6);
//  assert( configuration.fractionalSecondPrecision() == 6);
//  assert(!configuration.omitColonInZoneDesignator());
//  assert(!configuration.useCommaForDecimalSign());
//  assert( configuration.useZAbbreviationForUtc());
//..
// Now, we set the default configuration to the value of our 'configuration'
// object:
//..
//  bdlt::Iso8601UtilConfiguration::setDefaultConfiguration(configuration);
//..
// Finally, we verify that the default configuration was updated as expected:
//..
//  const bdlt::Iso8601UtilConfiguration newConfiguration =
//                      bdlt::Iso8601UtilConfiguration::defaultConfiguration();
//  assert( newConfiguration.fractionalSecondPrecision() == 6);
//  assert(!newConfiguration.omitColonInZoneDesignator());
//  assert(!newConfiguration.useCommaForDecimalSign());
//  assert( newConfiguration.useZAbbreviationForUtc());
//..
// Note that the expected usage is that the process-wide configuration will be
// established *once*, early in 'main', and not changed throughout the lifetime
// of a process.

#include <bdlscm_version.h>

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

#include <bsl_iosfwd.h>

namespace BloombergLP {
namespace bdlt {

                      // ==============================
                      // class Iso8601UtilConfiguration
                      // ==============================

class Iso8601UtilConfiguration {
    // This unconstrained (value-semantic) attribute class characterizes how to
    // configure certain behavior in 'Iso8601Util' functions.  Currently, only
    // the 'generate' and 'generateRaw' methods of that utility are affected by
    // 'Iso8601UtilConfiguration' settings.  See the Attributes section under
    // @DESCRIPTION in the component-level documentation for information on the
    // class attributes.

  private:
    // PRIVATE TYPES
    enum {
        // This enumeration denotes the distinct bits that define the values of
        // each of the four configuration attributes.

        k_FRACTIONAL_SECOND_PRECISION_MASK  = 0x07,
        k_OMIT_COLON_IN_ZONE_DESIGNATOR_BIT = 0x08,
        k_USE_COMMA_FOR_DECIMAL_SIGN_BIT    = 0x10,
        k_USE_Z_ABBREVIATION_FOR_UTC_BIT    = 0x20
    };

    // CLASS DATA
    static bsls::AtomicOperations::AtomicTypes::Int
                    s_defaultConfiguration;  // process-wide configuration

    // DATA
    int             d_configurationMask;     // bitmask defining configuration

    // FRIENDS
    friend bool operator==(const Iso8601UtilConfiguration&,
                           const Iso8601UtilConfiguration&);
    friend bool operator!=(const Iso8601UtilConfiguration&,
                           const Iso8601UtilConfiguration&);

  private:
    // PRIVATE CREATORS
    explicit Iso8601UtilConfiguration(int configurationMask);
        // Create an 'Iso8601UtilConfiguration' object having the value
        // indicated by the specified 'configurationMask'.  The behavior is
        // undefined unless 'configurationMask' represents a valid
        // 'Iso8601UtilConfiguration' value.

  public:
    // CLASS METHODS
    static Iso8601UtilConfiguration defaultConfiguration();
        // Return the value of the process-wide 'Iso8601UtilConfiguration' that
        // is currently in effect.

    static void setDefaultConfiguration(
                                const Iso8601UtilConfiguration& configuration);
        // Set the value of the process-wide 'Iso8601UtilConfiguration' to the
        // specified 'configuration'.  Note that the expected usage is that the
        // process-wide configuration will be established *once*, early in
        // 'main', and not changed throughout the lifetime of a process.

    // CREATORS
    Iso8601UtilConfiguration();
        // Create an 'Iso8601UtilConfiguration' object having the (default)
        // attribute values:
        //..
        //  fractionalSecondPrecision() == 3
        //  omitColonInZoneDesignator() == false
        //  useCommaForDecimalSign()    == false
        //  useZAbbreviationForUtc()    == false
        //..

    Iso8601UtilConfiguration(const Iso8601UtilConfiguration& original);
        // Create an 'Iso8601UtilConfiguration' object having the value of the
        // specified 'original' configuration.

    ~Iso8601UtilConfiguration();
        // Destroy this object.

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

    void setFractionalSecondPrecision(int value);
        // Set the 'fractionalSecondPrecision' attribute of this object to the
        // specified 'value'.  The behavior is undefined unless '0 <= value'
        // and '6 >= value'.

    void setOmitColonInZoneDesignator(bool value);
        // Set the 'omitColonInZoneDesignator' attribute of this object to the
        // specified 'value'.

    void setUseCommaForDecimalSign(bool value);
        // Set the 'useCommaForDecimalSign' attribute of this object to the
        // specified 'value'.

    void setUseZAbbreviationForUtc(bool value);
        // Set the 'useZAbbreviationForUtc' attribute of this object to the
        // specified 'value'.

    // ACCESSORS
    int fractionalSecondPrecision() const;
        // Return the value of the 'fractionalSecondPrecision' attribute of
        // this object.

    bool omitColonInZoneDesignator() const;
        // Return the value of the 'omitColonInZoneDesignator' attribute of
        // this object.

    bool useCommaForDecimalSign() const;
        // Return the value of the 'useCommaForDecimalSign' attribute of this
        // object.

    bool useZAbbreviationForUtc() const;
        // Return the value of the 'useZAbbreviationForUtc' attribute of this
        // object.

                                  // Aspects

    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 this
        // human-readable format is not fully specified, and can change without
        // notice.
};

// FREE OPERATORS
bool operator==(const Iso8601UtilConfiguration& lhs,
                const Iso8601UtilConfiguration& rhs);
    // Return 'true' if the specified 'lhs' and 'rhs' objects have the same
    // value, and 'false' otherwise.  Two 'Iso8601UtilConfiguration' objects
    // have the same value if each of their 'fractionalSecondPrecision',
    // 'omitColonInZoneDesignator', 'useCommaForDecimalSign', and
    // 'useZAbbreviationForUtc' attributes (respectively) have the same value.

bool operator!=(const Iso8601UtilConfiguration& lhs,
                const Iso8601UtilConfiguration& rhs);
    // Return 'true' if the specified 'lhs' and 'rhs' objects do not have the
    // same value, and 'false' otherwise.  Two 'Iso8601UtilConfiguration'
    // objects do not have the same value if any of their
    // 'fractionalSecondPrecision', 'omitColonInZoneDesignator',
    // 'useCommaForDecimalSign', or 'useZAbbreviationForUtc' attributes
    // (respectively) do not have the same value.

bsl::ostream& operator<<(bsl::ostream&                   stream,
                         const Iso8601UtilConfiguration& 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 and can change
    // without notice.  Also note that this method has the same behavior as
    // 'object.print(stream, 0, -1)', but with the attribute names elided.

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

                      // ------------------------------
                      // class Iso8601UtilConfiguration
                      // ------------------------------

// PRIVATE CREATORS
inline
Iso8601UtilConfiguration::Iso8601UtilConfiguration(int configurationMask)
: d_configurationMask(configurationMask)
{
    BSLS_ASSERT(0 == (configurationMask
                           & ~(k_FRACTIONAL_SECOND_PRECISION_MASK
                             | k_OMIT_COLON_IN_ZONE_DESIGNATOR_BIT
                             | k_USE_COMMA_FOR_DECIMAL_SIGN_BIT
                             | k_USE_Z_ABBREVIATION_FOR_UTC_BIT)));
}

// CLASS METHODS
inline
Iso8601UtilConfiguration Iso8601UtilConfiguration::defaultConfiguration()
{
    return Iso8601UtilConfiguration(
               bsls::AtomicOperations::getIntRelaxed(&s_defaultConfiguration));
}

inline
void Iso8601UtilConfiguration::setDefaultConfiguration(
                                 const Iso8601UtilConfiguration& configuration)
{
    bsls::AtomicOperations::setIntRelaxed(&s_defaultConfiguration,
                                          configuration.d_configurationMask);
}

// CREATORS
inline
Iso8601UtilConfiguration::Iso8601UtilConfiguration()
: d_configurationMask(3)
{
}

inline
Iso8601UtilConfiguration::Iso8601UtilConfiguration(
                                      const Iso8601UtilConfiguration& original)
: d_configurationMask(original.d_configurationMask)
{
}

inline
Iso8601UtilConfiguration::~Iso8601UtilConfiguration()
{
    BSLS_ASSERT(0 == (d_configurationMask
                           & ~(k_FRACTIONAL_SECOND_PRECISION_MASK
                             | k_OMIT_COLON_IN_ZONE_DESIGNATOR_BIT
                             | k_USE_COMMA_FOR_DECIMAL_SIGN_BIT
                             | k_USE_Z_ABBREVIATION_FOR_UTC_BIT)));
}

// MANIPULATORS
inline
Iso8601UtilConfiguration& Iso8601UtilConfiguration::operator=(
                                           const Iso8601UtilConfiguration& rhs)
{
    d_configurationMask = rhs.d_configurationMask;

    return *this;
}

// ACCESSORS
inline
int Iso8601UtilConfiguration::fractionalSecondPrecision() const
{
    return d_configurationMask & k_FRACTIONAL_SECOND_PRECISION_MASK;
}

inline
bool Iso8601UtilConfiguration::omitColonInZoneDesignator() const
{
    return d_configurationMask & k_OMIT_COLON_IN_ZONE_DESIGNATOR_BIT;
}

inline
bool Iso8601UtilConfiguration::useCommaForDecimalSign() const
{
    return d_configurationMask & k_USE_COMMA_FOR_DECIMAL_SIGN_BIT;
}

inline
bool Iso8601UtilConfiguration::useZAbbreviationForUtc() const
{
    return d_configurationMask & k_USE_Z_ABBREVIATION_FOR_UTC_BIT;
}

}  // close package namespace

// FREE OPERATORS
inline
bool bdlt::operator==(const Iso8601UtilConfiguration& lhs,
                      const Iso8601UtilConfiguration& rhs)
{
    return lhs.d_configurationMask == rhs.d_configurationMask;
}

inline
bool bdlt::operator!=(const Iso8601UtilConfiguration& lhs,
                      const Iso8601UtilConfiguration& rhs)
{
    return lhs.d_configurationMask != rhs.d_configurationMask;
}

}  // close enterprise namespace

#endif

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