// ball_attributecontainer.h                                          -*-C++-*-
#ifndef INCLUDED_BALL_ATTRIBUTECONTAINER
#define INCLUDED_BALL_ATTRIBUTECONTAINER

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

//@PURPOSE: Provide a protocol for containers holding logging attributes.
//
//@CLASSES:
//  ball::AttributeContainer: a protocol for a collection of attributes
//
//@SEE_ALSO: ball_attribute, ball_defaultattributecontainer
//
//@DESCRIPTION: This component defines a protocol class,
// `ball::AttributeContainer`, for containers of `ball::Attribute` values.  The
// `ball::AttributeContainer` protocol primarily provides a `hasValue()`
// method, allowing clients to determine if a given attribute value is held by
// the container.
//
// This component participates in the implementation of "Rule-Based Logging".
// For more information on how to use that feature, please see the package
// level documentation and usage examples for "Rule-Based Logging".
//
///Usage
///-----
// In the following examples we examine two derived implementations of the
// `ball::AttributeContainer` protocol.  The first implementation is
// potentially more efficient, holding a specific group of attributes relevant
// to a particular application.  The second implementation is more general,
// and can hold any valid `ball::Attribute` value.  In the final example we
// demonstrate how to call the methods of the `ball::AttributeContainer`
// protocol.
//
///Example 1: An Implementation of `ball::AttributeContainer`
/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// In the following example we develop a `ball::AttributeContainer`
// implementation specifically intended for a service offline that will perform
// rule-based logging, governed by the service client's Bloomberg "uuid",
// "luw", and "firm number".  We define a class `ServiceAttributes` that
// contains three integer `ball::Attribute` values having the names "uuid",
// "luw", and "firmNumber".
//
// Note that this implementation requires no memory allocation, so it will be
// more efficient than a more general set-based implementation if the container
// is frequently created, destroyed, or modified.  We will develop a
// `ball::AttributeContainer` implementation that can hold any
// `ball::Attribute` value in example 2 (and one is provided by the `ball`
// package in the `ball_defaultattributecontainer` component).
// ```
// // serviceattributes.h
//
// /// Provide a concrete implementation of the `ball::AttributeContainer`
// /// that holds the `uuid`, `luw`, and `firmNumber` associated with a
// /// request to the example service.  This concrete container
// /// exposes those properties in attributes named "uuid", "luw", and
// /// "firmNumber" respectively.
// class ServiceAttributes : public ball::AttributeContainer {
//
// ```
// Note that we use the type `ball::Attribute` for our data members for
// simplicity.  It would be a little more efficient to use `int` data members,
// but the implementation of `hasValue()` would be less readable.
// ```
//     // DATA
//     ball::Attribute d_uuid;        // Bloomberg user id
//     ball::Attribute d_luw;         // Bloomberg luw
//     ball::Attribute d_firmNumber;  // Bloomberg firm number
//
//     // ...
//
//   public:
//     // PUBLIC CONSTANTS
//
//     // The names of the attributes exposed by this attribute container.
//     static const char * const UUID_ATTRIBUTE_NAME;
//     static const char * const LUW_ATTRIBUTE_NAME;
//     static const char * const FIRMNUMBER_ATTRIBUTE_NAME;
//
//     // CREATORS
//
//     /// Create a service-attributes object with the specified `uuid`,
//     /// `luw`, and `firmNumber`.
//     ServiceAttributes(int uuid, int luw, int firmNumber);
//
//     virtual ~ServiceAttributes();
//         // Destroy this service-attributes object.
//
//     // ACCESSORS
//
//     /// Return `true` if the attribute having specified `value` exists
//     /// in this object, and `false` otherwise.  This implementation will
//     /// return `true` if `value.name()` equals "uuid", "luw", or "firm"
//     /// and `value.value()` is an `int` equal to the corresponding
//     /// property value supplied at construction.
//     virtual bool hasValue(const ball::Attribute& value) const;
//
//     /// Format this object to the specified output `stream`.
//     virtual bsl::ostream& print(bsl::ostream& stream,
//                                 int           level = 0,
//                                 int           spacesPerLevel = 4) const;
//
//     /// Invoke the specified `visitor` function for all attributes in
//     /// this container.
//     virtual void visitAttributes(
//          const bsl::function<void(const ball::Attribute&)>& visitor) const;
// };
//
// // CREATORS
// inline
// ServiceAttributes::ServiceAttributes(int uuid, int luw, int firmNumber)
// : d_uuid(UUID_ATTRIBUTE_NAME, uuid)
// , d_luw(LUW_ATTRIBUTE_NAME, luw)
// , d_firmNumber(FIRMNUMBER_ATTRIBUTE_NAME, firmNumber)
// {
// }
//
//   // serviceattributes.cpp
//
// // PUBLIC CONSTANTS
// const char * const ServiceAttributes::UUID_ATTRIBUTE_NAME       = "uuid";
// const char * const ServiceAttributes::LUW_ATTRIBUTE_NAME        = "luw";
// const char * const ServiceAttributes::FIRMNUMBER_ATTRIBUTE_NAME =
//                                                               "firmNumber";
//
// // CREATORS
// ServiceAttributes::~ServiceAttributes()
// {
// }
//
// // ACCESSORS
// bool ServiceAttributes::hasValue(const ball::Attribute& value) const
// {
//     return d_uuid == value || d_luw == value || d_firmNumber == value;
// }
//
// bsl::ostream& ServiceAttributes::print(bsl::ostream& stream,
//                                        int           level,
//                                        int           spacesPerLevel) const
// {
//     bslim::Printer printer(&stream, level, spacesPerLevel);
//     printer.start();
//     printer.printAttribute("uuid", d_uuid);
//     printer.printAttribute("luw", d_luw);
//     printer.printAttribute("firmNumber", d_firmNumber);
//     printer.end();
//     return stream;
// }
//
// void ServiceAttributes::visitAttributes(
//           const bsl::function<void(const ball::Attribute&)> &visitor) const
// {
//     visitor(d_uuid);
//     visitor(d_luw);
//     visitor(d_firmNumber);
// }
// ```
//
///Example 2: A Generic Implementation of `ball::AttributeContainer`
///- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// In this second example we define a `ball::AttributeContainer` that can
// contain any valid `ball::Attribute` value (a "generic"
// `ball::AttributeContainer`).  In practice, an implementation that can
// contain any attribute values may be less efficient than one specifically
// created for a particular group of attributes needed by an application (as
// shown in {Example 1}).
//
// Note that the `ball` package provides a similar `ball::AttributeContainer`
// implementation in the `ball_defaultattributecontainer` component.
// ```
// // attributeset.h
//
// /// A simple set-based implementation of the `ball::AttributeContainer`
// /// protocol, used for testing.
// class AttributeSet : public ball::AttributeContainer {
// ```
// To define an STL set (or hash set) for `ball::Attribute` values, we must
// define a comparison (or hash) operation for attribute values.  Here we
// define a comparison functor that compares attributes by name, then by
// value-type, and finally by value.
// ```
//     struct AttributeComparator {
//         /// Return `true` if the specified `lhs` attribute is ordered
//         /// before the specified `rhs` attribute, and `false` otherwise.
//         bool operator()(const ball::Attribute& lhs,
//                         const ball::Attribute& rhs) const
//         {
//             int cmp = bsl::strcmp(lhs.name(), rhs.name());
//             if (0 != cmp) {
//                 return cmp < 0;
//             }
//             if (lhs.value().typeIndex() != rhs.value().typeIndex()) {
//                 return lhs.value().typeIndex() < rhs.value().typeIndex();
//             }
//             switch (lhs.value().typeIndex()) {
//               case 0: // unset
//                 return true;
//               case 1: // int
//                 return lhs.value().the<int>() < rhs.value().the<int>();
//               case 2: // long
//                 return lhs.value().the<long>() < rhs.value().the<long>();
//               case 3: // long long
//                 return lhs.value().the<long long>()
//                      < rhs.value().the<long long>();
//               case 4: // unsigned int
//                 return lhs.value().the<unsigned int>()
//                      < rhs.value().the<unsigned int>();
//               case 5: // unsigned long
//                 return lhs.value().the<unsigned long>()
//                      < rhs.value().the<unsigned long>();
//               case 6: // unsigned long long
//                 return lhs.value().the<unsigned long long>()
//                      < rhs.value().the<unsigned long long>();
//               case 7: // string
//                 return lhs.value().the<bsl::string>() <
//                        rhs.value().the<bsl::string>();
//               case 8: // const void *
//                 return lhs.value().the<const void *>() <
//                        rhs.value().the<const void *>();
//               case 9: // Guid
//                 return lhs.value().the<bdlb::Guid>() <
//                        rhs.value().the<bdlb::Guid>();
//             }
//             BSLS_ASSERT(false);
//             return false;
//         }
//     };
//
//     // DATA
//     bsl::set<ball::Attribute, AttributeComparator> d_set;
//
//   public:
//     // CREATORS
//
//     /// Create an attribute set.
//     AttributeSet(bslma::Allocator *basicAllocator = 0);
//
//     /// Destroy this attribute set.
//     virtual ~AttributeSet();
//
//     // MANIPULATORS
//
//     /// Add the specified value to this attribute set.
//     void insert(const ball::Attribute& value);
//
//     /// Remove the specified value from this attribute set.  Return
//     /// `true` if the attribute was found, and `false` if `value` was
//     /// not a member of this set.
//     bool remove(const ball::Attribute& value);
//
//     // ACCESSORS
//
//     /// Return `true` if the attribute having specified `value` exists
//     /// in this object, and `false` otherwise.
//     virtual bool hasValue(const ball::Attribute& value) 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`.
//     virtual bsl::ostream& print(bsl::ostream& stream,
//                                 int           level = 0,
//                                 int           spacesPerLevel = 4) const;
//
//     /// Invoke the specified `visitor` function for all attributes in
//     /// this container.
//     virtual void visitAttributes(
//          const bsl::function<void(const ball::Attribute&)>& visitor) const;
// };
// ```
// The `AttributeSet` methods are simple wrappers around `bsl::set` methods:
// ```
// inline
// AttributeSet::AttributeSet(bslma::Allocator *basicAllocator)
// : d_set(AttributeComparator(), basicAllocator)
// {
// }
//
// // MANIPULATORS
// inline
// void AttributeSet::insert(const ball::Attribute& value)
// {
//     d_set.insert(value);
// }
//
// inline
// bool AttributeSet::remove(const ball::Attribute& value)
// {
//     return d_set.erase(value) > 0;
// }
//
// // attributeset.cpp
//
// // CREATORS
// AttributeSet::~AttributeSet()
// {
// }
//
// // ACCESSORS
// bool AttributeSet::hasValue(const ball::Attribute& value) const
// {
//     return d_set.find(value) != d_set.end();
// }
//
// bsl::ostream& AttributeSet::print(bsl::ostream& stream,
//                                   int           level,
//                                   int           spacesPerLevel) const
// {
//     bslim::Printer printer(&stream, level, spacesPerLevel);
//     printer.start();
//
//     bsl::set<ball::Attribute, AttributeComparator>::const_iterator it
//                                                            = d_set.begin();
//     for (; it != d_set.end(); ++it) {
//         printer.printValue(*it);
//     }
//     printer.end();
//
//     return stream;
// }
//
// void AttributeSet::visitAttributes(
//           const bsl::function<void(const ball::Attribute&)> &visitor) const
// {
//     bsl::set<ball::Attribute, AttributeComparator>::const_iterator it
//                                                            = d_set.begin();
//     for (; it != d_set.end(); ++it) {
//         visitor(*it);
//     }
// }
// ```
//
///Example 3. Using a `ball::AttributeContainer`
///- - - - - - - - - - - - - - - - - - - - - - -
// In this final example, we demonstrate how to call the methods of the
// `ball::AttributeContainer` protocol.
//
// First we create an object of a concrete type that implements the
// `ball::AttributeContainer` protocol (e.g., `ServiceAttributes` defined in
// {Example 1}).  Then we obtain a reference to this object.
// ```
// ServiceAttributes serviceAttributes(3938908, 1, 9001);
// const ball::AttributeContainer& attributes = serviceAttributes;
// ```
// We use `hasValue()` to examine the values in the container:
// ```
// assert(true == attributes.hasValue(ball::Attribute("uuid", 3938908)));
// assert(true == attributes.hasValue(ball::Attribute("luw",  1)));
// assert(true == attributes.hasValue(ball::Attribute("firmNumber", 9001)));
//
// assert(false == attributes.hasValue(ball::Attribute("uuid", 1391015)));
// assert(false == attributes.hasValue(ball::Attribute("luw",  2)));
// assert(false == attributes.hasValue(ball::Attribute("bad name", 3938908)));
// ```
// Finally we can print the attribute values in the container:
// ```
// bsl::cout << attributes << bsl::endl;
// ```
// The resulting output should look like:
// ```
// [ [ uuid = 3938908 ] [ luw = 1 ] [ firmNumber = 9001 ] ]
// ```

#include <balscm_version.h>

#include <bsl_functional.h>
#include <bsl_iosfwd.h>

namespace BloombergLP {
namespace ball {

class Attribute;

                    // ========================
                    // class AttributeContainer
                    // ========================

/// This class defines a protocol for a container of attribute values.  The
/// attribute container provides a `hasValue()` method to determine whether
/// an attribute value is in the container.
class AttributeContainer {

  public:
    // CREATORS

    /// Destroy this object.
    virtual ~AttributeContainer();

    // ACCESSORS

    /// Return `true` if a attribute having the specified `value` exists in
    /// this object, and `false` otherwise.
    virtual bool hasValue(const Attribute& value) const = 0;

    /// 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.
    virtual bsl::ostream& print(bsl::ostream& stream,
                                int           level = 0,
                                int           spacesPerLevel = 4) const = 0;

    /// Invoke the specified `visitor` function for all attributes in this
    /// container.
    virtual void visitAttributes(
         const bsl::function<void(const ball::Attribute&)>& visitor) const = 0;
};

// FREE OPERATORS

/// Write the value of the specified `container` to the specified output
/// `stream` in a single-line format, and return a reference to the
/// modifiable stream.
bsl::ostream& operator<<(bsl::ostream&             stream,
                         const AttributeContainer& container);

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

                    // ------------------------
                    // class AttributeContainer
                    // ------------------------

}  // close package namespace

// FREE OPERATORS
inline
bsl::ostream& ball::operator<<(bsl::ostream&             stream,
                               const AttributeContainer& container)
{
    return container.print(stream, 0, -1);
}

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