// balm_metricdescription.h                                           -*-C++-*-

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

#ifndef INCLUDED_BALM_METRICDESCRIPTION
#define INCLUDED_BALM_METRICDESCRIPTION

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

//@PURPOSE: Provide a description for a metric.
//
//@CLASSES:
//   balm::MetricDescription: describes a metric
//
//@SEE_ALSO: balm_metricregistry, balm_metricid, balm_category
//
//@DESCRIPTION: This component provides a class, 'balm::MetricDescription',
// used to describe a metric.  A 'balm::MetricDescription' object contains the
// address of the category to which the metric belongs and also the address of
// the null-terminated string holding the name of the metric.  The
// 'balm::MetricDescription' class suppresses copy construction and assignment,
// and does not provide equality operators: Applications should use a *single*
// 'balm::MetricDescription' object per metric (such as one provided by the
// *'balm::MetricRegistry'* component).
//
// IMPORTANT: The metric description's 'name', whose type is 'const char *',
// must remain constant and valid throughout the lifetime of the
// 'balm::MetricDescription' object.
//
///Alternative Systems for Telemetry
///---------------------------------
// Bloomberg software may alternatively use the GUTS telemetry API, which is
// integrated into Bloomberg infrastructure.
//
///Thread Safety
///-------------
// 'balm::MetricDescription' is *const* *thread-safe*, meaning that accessors
// may be invoked concurrently from different threads, but it is not safe to
// access or modify a 'balm::MetricDescription' in one thread while another
// thread modifies the same object.  However, clients of the 'balm' package
// accessing a non-modifiable 'balm::MetricDescription' supplied by a
// 'balm::MetricRegistry' (by way of a 'balm::MetricId') can safely access the
// properties of that metric description at any time.
//
///Usage
///-----
// The following example demonstrates how to create and access a
// 'balm::MetricDescription' object.  We start by creating a category:
//..
//  balm::Category myCategory("MyCategory");
//..
// Then we use that category to create three metric description objects with
// different names:
//..
//  balm::MetricDescription metricA(&myCategory, "A");
//  balm::MetricDescription metricB(&myCategory, "B");
//  balm::MetricDescription metricC(&myCategory, "C");
//..
// We can use the 'category' and 'name' methods to access their values:
//..
//  assert(&myCategory == metricA.category());
//  assert(&myCategory == metricB.category());
//  assert(&myCategory == metricC.category());
//
//  assert(0 == bsl::strcmp("A", metricA.name()));
//  assert(0 == bsl::strcmp("B", metricB.name()));
//  assert(0 == bsl::strcmp("C", metricC.name()));
//..
// Finally, we write all three metric descriptions to the console:
//..
//  bsl::cout << "metricA: " << metricA << bsl::endl
//            << "metricB: " << metricB << bsl::endl
//            << "metricC: " << metricC << bsl::endl;
//..
// With the following console output:
//..
//  metricA: MyCategory.A
//  metricB: MyCategory.B
//  metricC: MyCategory.C
//..

#include <balscm_version.h>

#include <balm_publicationtype.h>

#include <bslmt_lockguard.h>
#include <bslmt_mutex.h>

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

#include <bsl_iosfwd.h>
#include <bsl_memory.h>

namespace BloombergLP {


namespace balm {

class Category;
class MetricFormat;

                          // =======================
                          // class MetricDescription
                          // =======================

class MetricDescription {
    // This class provides a mechanism for describing a metric.  A
    // 'MetricDescription' holds the category to which the metric belongs, and
    // a null-terminated string containing the name of the metric.

    // DATA
    const Category *d_category_p;  // category of metric (held, not owned)

    const char     *d_name_p;      // name of metric (held, not owned)

    PublicationType::Value
                    d_preferredPublicationType;
                                   // preferred publication type

    bsl::shared_ptr<const MetricFormat>
                    d_format;      // format for this metric

    bsl::vector<const void *>
                    d_userData;    // user data, indexed by keys

    mutable bslmt::Mutex
                    d_mutex;       // synchronize non-'const' elements
                                   // (publication type, format, user data)

    // NOT IMPLEMENTED
    MetricDescription(const MetricDescription&);
    MetricDescription& operator=(const MetricDescription&);

  public:
    // PUBLIC TYPES
    typedef int UserDataKey;
        // A key used to refer to a data value associated with a metric.  Note
        // that a 'UserDataKey' can be used by clients of 'balm' to associate
        // additional information with a metric.  See 'balm_metricregistry'
        // for information on obtaining a unique key.

    // CREATORS
    MetricDescription(const Category   *category,
                      const char       *name,
                      bslma::Allocator *basicAllocator = 0);
        // Create a metric description for the specified 'category' and the
        // specified 'name'.  Optionally specify a 'basicAllocator' used to
        // supply memory.  If 'basicAllocator' is 0, the currently installed
        // default allocator is used.  The initial value for
        // 'preferredPublicationType' is 'e_UNSPECIFIED', and the initial
        // value for 'format' is 0.  The behavior is undefined unless 'name'
        // and 'category' remain valid, and the contents of 'name' remain
        // unmodified, for the lifetime of this object.

    // ~MetricDescription();
        // Destroy this metric description.  Note that this trivial destructor
        // is generated by the compiler.

    // MANIPULATORS
    void setName(const char *name);
        // Set the name of this metric description to the specified 'name'.
        // The behavior is undefined unless the contents of 'name' remains
        // valid and unmodified for the lifetime of this object.

    void setCategory(const Category *category);
        // Set the category of this metric description to the object at the
        // specified 'category' address.  The behavior is undefined unless
        // 'category' remains valid for the lifetime of this object.

    void setPreferredPublicationType(PublicationType::Value type);
        // Set the preferred publication type of this metric to the specified
        // 'type'.  The preferred publication type of this metric indicates the
        // preferred aggregate to publish for this metric, or
        // 'PublicationType::UNSPECIFIED' if there is no preference.  Note
        // that there is no uniform definition for how publishers will
        // interpret this value; an 'UNSPECIFIED' value generally indicates
        // that all the collected aggregates (total, count, minimum, and
        // maximum value) should be published.

    void setFormat(const bsl::shared_ptr<const MetricFormat>& format);
        // Set the format for this metric description to the specified
        // 'format'.

    void setUserData(UserDataKey key, const void *value);
        // Associate the specified 'value' with the specified data 'key'.  The
        // behavior is undefined unless 'key >= 0'.  Note that this method
        // allows clients of 'balm' to associate (opaque) application-specific
        // information with a metric.

    // ACCESSORS
    const char *name() const;
        // Return the address of the non-modifiable, null-terminated string
        // containing the name of the described metric.

    const Category *category() const;
        // Return the address of the non-modifiable category object indicating
        // the category of the metric described by this object.

    PublicationType::Value preferredPublicationType() const;
        // Return the preferred publication type of this metric.  The
        // preferred publication type of this metric indicates the preferred
        // aggregate to publish for this metric, or
        // 'PublicationType::UNSPECIFIED' if there is no preference.  Note
        // that there is no uniform definition for how publishers will
        // interpret this value; an 'UNSPECIFIED' value generally indicates
        // that the all the collected aggregates (total, count, minimum, and
        // maximum value) should be published.

    bsl::shared_ptr<const MetricFormat> format() const;
        // Return a shared pointer to the non-modifiable format for this
        // metric description.  Note that the returned shared pointer *may*
        // *be* *null* if a format has not been provided for the described
        // metric.

    const void *userData(UserDataKey key) const;
        // Return the non-modifiable value associated with the specified
        // user-data 'key'.  If the data for 'key' has not been set, a value of
        // 0 is returned, which is indistinguishable from a valid 'key' with a
        // 0 value.  The behavior is undefined unless 'key >= 0'.    Note that
        // this method allows clients of 'balm' to access the (opaque)
        // application-specific information that they have previously
        // associated with a metric (via 'setUserData').

    bsl::ostream& print(bsl::ostream& stream) const;
        // Print the category and name of this metric description to the
        // specified output 'stream' in some single-line human-readable form,
        // and return a reference to the modifiable 'stream'.

    bsl::ostream& printDescription(bsl::ostream& stream) const;
        // Print the properties of this metric description to the specified
        // output 'stream' in some single-line human-readable form, and return
        // a reference to the modifiable 'stream'.
};

// FREE OPERATORS
bsl::ostream& operator<<(bsl::ostream&            stream,
                         const MetricDescription& rhs);
    // Write a formatted single-line description of the specified 'rhs' metric
    // description to the specified 'stream', and return a reference to the
    // modifiable 'stream'.

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

                          // -----------------------
                          // class MetricDescription
                          // -----------------------

// CREATORS
inline
MetricDescription::MetricDescription(const Category   *category,
                                     const char       *name,
                                     bslma::Allocator *basicAllocator)
: d_category_p(category)
, d_name_p(name)
, d_preferredPublicationType(PublicationType::e_UNSPECIFIED)
, d_format()
, d_userData(basicAllocator)
, d_mutex()
{
}

// MANIPULATORS
inline
void MetricDescription::setName(const char *name)
{
    d_name_p = name;
}

inline
void MetricDescription::setCategory(const Category *category)
{
    d_category_p = category;
}

inline
void MetricDescription::setPreferredPublicationType(
                                              PublicationType::Value type)
{
    // This guard is not strictly required on any supported platform.
    bslmt::LockGuard<bslmt::Mutex> guard(&d_mutex);
    d_preferredPublicationType = type;
}

inline
void MetricDescription::setFormat(
                        const bsl::shared_ptr<const MetricFormat>& format)
{
    bslmt::LockGuard<bslmt::Mutex> guard(&d_mutex);
    d_format = format;
}

inline
void MetricDescription::setUserData(UserDataKey key, const void *value)
{
    BSLS_ASSERT(key >= 0);

    bslmt::LockGuard<bslmt::Mutex> guard(&d_mutex);
    if ((unsigned int)key >= d_userData.size()) {
        d_userData.resize(key + 1, 0);
    }
    d_userData[key] = value;
}

// ACCESSORS
inline
const char *MetricDescription::name() const
{
    return d_name_p;
}

inline
const Category *MetricDescription::category() const
{
    return d_category_p;
}

inline
PublicationType::Value
MetricDescription::preferredPublicationType() const
{
    // This guard is not strictly required on any supported platform.
    bslmt::LockGuard<bslmt::Mutex> guard(&d_mutex);
    return d_preferredPublicationType;
}

inline
bsl::shared_ptr<const MetricFormat>
MetricDescription::format() const

{
    bslmt::LockGuard<bslmt::Mutex> guard(&d_mutex);
    return d_format;
}

inline
const void *MetricDescription::userData(UserDataKey key) const
{
    BSLS_ASSERT(key >= 0);
    bslmt::LockGuard<bslmt::Mutex> guard(&d_mutex);
    return ((unsigned int)key < d_userData.size()) ? d_userData[key] : 0;
}

}  // close package namespace

// FREE OPERATORS
inline
bsl::ostream& balm::operator<<(bsl::ostream&      stream,
                         const MetricDescription& rhs)
{
    return rhs.print(stream);
}

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