// ball_attributecontext.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_BALL_ATTRIBUTECONTEXT
#define INCLUDED_BALL_ATTRIBUTECONTEXT

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

//@PURPOSE: Provide a container for storing attributes and caching results.
//
//@CLASSES:
//         ball::AttributeContext: thread-local list of attribute containers
//  ball::AttributeContextProctor: proctor for deleting an attribute context
//
//@SEE_ALSO: ball_attributecontainer
//
//@DESCRIPTION: This component provides a mechanism, 'ball::AttributeContext',
// used for storing attributes in thread-local storage and evaluating rules
// associated with a given category using those stored attributes, and a scoped
// proctor, 'ball::AttributeContextProctor', used for destroying the attribute
// context of the current thread.
//
// 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".
//
// Clients obtain the attribute context for the current thread by calling the
// 'getContext' class method.  Attributes are added and removed from an
// attribute context using the 'addAttributes' and 'removeAttributes' methods,
// respectively.  Additionally, 'ball::AttributeContext' provides methods, used
// primarily by other components in the 'ball' package' (see {Active Rules}
// below), to determine the effect of the current logging rules on the logging
// thresholds of a category.
//
// 'ball::AttributeContext' contains class data members that must be
// initialized, using the 'initialize' class method, with a 'CategoryManager'
// object containing a 'RuleSet' representing the currently installed (global)
// logging rules for the process.  However, clients generally should not call
// 'initialize' directly.  Instead, 'initialize' is called *internally* when
// the logger manager singleton is initialized.
//
///Active Rules
///------------
// 'ball::AttributeContext' provides two methods, 'hasRelevantActiveRules' and
// 'determineThresholdLevels', that are used to determine the effect of the
// current logging rules maintained by the category manager on the logging
// thresholds of a given category.  Note that these methods are generally
// intended for use by other components in the 'ball' package'.
//
// 'hasRelevantActiveRules' returns 'true' if there is at least one relevant
// and active rule (in the global set of rules) that might modify the logging
// thresholds of the supplied 'category'.  A rule is "relevant" if the rule's
// pattern matches the category's name, and a rule is "active" if all the
// attributes defined for that rule are satisfied by the current thread's
// attributes (i.e., 'ball::Rule::evaluate' returns 'true' for the collection
// of attributes maintained for the current thread by the thread's
// 'ball::AttributeContext' object).
//
// 'determineThresholdLevels' returns the logging threshold levels for a
// category, factoring in any active rules that apply to the category that
// might override the category's thresholds.
//
///Usage
///-----
// This section illustrates the intended use of 'ball::AttributeContext'.
//
///Example 1: Managing Attributes
///- - - - - - - - - - - - - - -
// First we will define a thread function that will create and install two
// attributes.  Note that we will use the 'AttributeSet' implementation of the
// 'ball::AttributeContainer' protocol defined in the component documentation
// for 'ball_attributecontainer'; the 'ball' package provides a similar class
// in the 'ball_defaultattributecontainer' component.
//..
//  extern "C" void *workerThread1(void *)
//  {
//..
// Inside this thread function, we create an attribute set to hold our
// attribute values, then we create two 'ball::Attribute' objects and add them
// to that set:
//..
//      AttributeSet attributes;
//      ball::Attribute a1("uuid", 4044457);
//      ball::Attribute a2("name", "Gang Chen");
//      attributes.insert(a1);
//      attributes.insert(a2);
//..
// Next, we obtain a reference to the current thread's attribute context using
// the 'getContext' class method (note that in practice we would use a scoped
// guard for this purpose; see {'ball_scopedattributes'}):
//..
//      ball::AttributeContext *context = ball::AttributeContext::getContext();
//      assert(context);
//      assert(context == ball::AttributeContext::lookupContext());
//..
// We can add our attribute container, 'attributes', to the current context
// using the 'addAttributes' method.  We store the returned iterator so that
// we can remove 'attributes' before it goes out of scope and is destroyed:
//..
//      ball::AttributeContext::iterator it =
//                                         context->addAttributes(&attributes);
//      assert(context->hasAttribute(a1));
//      assert(context->hasAttribute(a2));
//..
// We then call the 'removeAttributes' method to remove the attributes from
// the attribute context:
//..
//      context->removeAttributes(it);
//      assert(false == context->hasAttribute(a1));
//      assert(false == context->hasAttribute(a2));
//..
// This completes the first thread function:
//..
//      return 0;
//  }
//..
// The second thread function will simply verify that there is no currently
// available attribute context.  Note that attribute contexts are created and
// managed by individual threads using thread-specific storage, and that
// attribute contexts created by one thread are not visible in any other
// threads:
//..
//  extern "C" void *workerThread2(void *)
//  {
//      assert(0 == ball::AttributeContext::lookupContext());
//      return 0;
//  }
//..
//
///Example 2: Calling 'hasRelevantActiveRules' and 'determineThresholdLevels'
///- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// In this example we demonstrate how to call the 'hasRelevantActiveRules' and
// 'determineThresholdLevels' methods.  These methods are used (primarily by
// other components in the 'ball' package) to determine the effect of the
// current logging rules on the logging thresholds of a category.  Note that a
// rule is "relevant" if the rule's pattern matches the category's name, and a
// rule is "active" if 'ball::Rule::evaluate' returns 'true' for the
// collection of attributes maintained for the current thread by the thread's
// 'ball::AttributeContext' object.
//
// We start by creating a 'ball::CategoryManager' and use it to initialize the
// static data members of 'ball::AttributeContext'.  Note that, in practice,
// this initialization should *not* be performed by clients of the 'ball'
// package: 'ball::AttributeContext::initialize' is called *internally* as part
// of the initialization of the 'ball::LoggerManager' singleton.
//..
//  ball::CategoryManager categoryManager;
//  ball::AttributeContext::initialize(&categoryManager);
//..
// Next, we add a category to the category manager.  Each created category has
// a name and the logging threshold levels for that category.  The logging
// threshold levels indicate the minimum severity for logged messages that will
// trigger the relevant action.  The four thresholds are the "record level"
// (messages logged with a higher severity than this threshold should be added
// to the current logger's record buffer), the "pass-through level" (messages
// logged with a severity higher than this threshold should be published
// immediately), the "trigger level" (messages logged with a higher severity
// than this threshold should trigger the publication of the entire contents of
// the current logger's record buffer), and the "trigger-all level" (messages
// logged with a higher severity than this threshold should trigger the
// publication of every logger's record buffer), respectively.  Note that
// clients are generally most interested in the "pass-through" threshold level.
// Also note that a higher number indicates a lower severity.
//..
//  const ball::Category *cat1 =
//                  categoryManager.addCategory("MyCategory", 128, 96, 64, 32);
//..
// Next, we obtain the context for the current thread:
//..
//  ball::AttributeContext *context = ball::AttributeContext::getContext();
//..
// We call 'hasRelevantActiveRules' on 'cat1'.  This will be 'false' because
// we haven't supplied any rules:
//..
//  assert(!context->hasRelevantActiveRules(cat1));
//..
// We call 'determineThresholdLevels' on 'cat1'.  This will simply return the
// logging threshold levels we defined for 'cat1' when it was created because
// no rules have been defined that might modify those thresholds:
//..
//  ball::ThresholdAggregate cat1ThresholdLevels(0, 0, 0, 0);
//  context->determineThresholdLevels(&cat1ThresholdLevels, cat1);
//  assert(128 == cat1ThresholdLevels.recordLevel());
//  assert( 96 == cat1ThresholdLevels.passLevel());
//  assert( 64 == cat1ThresholdLevels.triggerLevel());
//  assert( 32 == cat1ThresholdLevels.triggerAllLevel());
//..
// Next, we create a rule that will apply to those categories whose names match
// the pattern "My*", where '*' is a wild-card value.  The rule defines a set
// of thresholds levels that may override the threshold levels of those
// categories whose name matches the rule's pattern:
//..
//  ball::Rule myRule("My*", 120, 110, 70, 40);
//  categoryManager.addRule(myRule);
//..
// Now, we call 'hasRelevantActiveRules' again for 'cat1', but this time the
// method returns 'true' because the rule we just added is both "relevant" to
// 'cat1' and "active".  'myRule' is "relevant" to 'cat1' because the name of
// 'cat1' ("MyCategory") matches the pattern for 'myRule' ("My*") (i.e.,
// 'myRule' applies to 'cat1').  'myRule' is also "active" because all the
// attributes defined for the rule are satisfied by the current thread (in this
// case the rule has no attributes, so the rule is always "active").  Note
// that we will discuss the meaning of "active" and the use of attributes later
// in this example.
//..
//  assert(context->hasRelevantActiveRules(cat1));
//..
// Next, we call 'determineThresholdLevels' for 'cat1'.  This method compares
// the threshold levels defined for a category with those of any active rules
// that apply to that category, and determines the minimum severity (i.e., the
// maximum numerical value) for each respective threshold amongst those values.
//..
//  ball::ThresholdAggregate thresholdLevels(0, 0, 0, 0);
//  context->determineThresholdLevels(&thresholdLevels, cat1);
//  assert(128 == thresholdLevels.recordLevel());
//  assert(110 == thresholdLevels.passLevel());
//  assert( 70 == thresholdLevels.triggerLevel());
//  assert( 40 == thresholdLevels.triggerAllLevel());
//..
// In this case the "pass-through", "trigger", and "trigger-all" threshold
// levels defined by 'myRule' (110, 70, and 40) are greater (i.e., define a
// lower severity) than those respective values defined for 'cat1' (96, 64,
// and 32), so those values override the values defined for 'cat1'.  On the
// other hand, the "record" threshold level for 'cat1' (128) is greater than
// the value defined by 'myRule' (120), so the threshold level defined for
// 'cat1' is  returned.  In effect, 'myRule' has lowered the severity at which
// messages logged in the "MyCategory" category will be published immediately,
// trigger the publication of the current logger's record buffer, and trigger
// the publication of every logger's record buffer.
//
// Next we modify 'myRule', adding an attribute indicating that the rule should
// only apply if the attribute context for the current thread contains the
// attribute '("uuid", 3938908)':
//..
//  categoryManager.removeRule(myRule);
//  ball::ManagedAttribute attribute("uuid", 3938908);
//  myRule.addAttribute(attribute);
//  categorymanager.addRule(myRule);
//..
// When we again call 'hasRelevantActiveRules' for 'cat1', it now returns
// 'false'.  The rule, 'myRule', still applies to 'cat1' (i.e., it is still
// "relevant" to 'cat1'), but the attributes defined by 'myRule' are no longer
// satisfied by the current thread, i.e., the current thread's attribute
// context does not contain an attribute matching '("uuid", 3938908)'.
//..
//  assert(!context->hasRelevantActiveRules(cat1));
//..
// Next, we call 'determineThresholdLevels' on 'cat1' and find that it
// returns the threshold levels we defined for 'cat1' when we created it:
//..
//  context->determineThresholdLevels(&thresholdLevels, cat1);
//  assert(thresholdLevels == cat1ThresholdLevels);
//..
// Finally, we add an attribute to the current thread's attribute context (as
// we did in the first example, "Managing Attributes").  Note that we keep an
// iterator referring to the added attributes so that we can remove them before
// 'attributes' goes out of scope and is destroyed.  Also note that the class
// 'AttributeSet' is defined in the component documentation for
// 'ball_attributecontainer'.
//..
//  AttributeSet attributes;
//  attributes.insert(ball::Attribute("uuid", 3938908));
//  ball::AttributeContext::iterator it = context->addAttributes(&attributes);
//..
// The following call to 'hasRelevantActiveRules' will return 'true' for 'cat1'
// because there is at least one rule, 'myRule', that is both "relevant"
// (i.e., its pattern matches the category name of 'cat1') and "active" (i.e.,
// all of the attributes defined for 'myRule' are satisfied by the attributes
// held by this thread's attribute context):
//..
//  assert(context->hasRelevantActiveRules(cat1));
//..
// Now, when we call 'determineThresholdLevels', it will again return the
// maximum threshold level from 'cat1' and 'myRule':
//..
//  context->determineThresholdLevels(&thresholdLevels, cat1);
//  assert(128 == thresholdLevels.recordLevel());
//  assert(110 == thresholdLevels.passLevel());
//  assert( 70 == thresholdLevels.triggerLevel());
//  assert( 40 == thresholdLevels.triggerAllLevel());
//..
// We must be careful to remove 'attributes' from the attribute context before
// it goes out of scope and is destroyed.  Note that the 'ball' package
// provides a component, 'ball_scopedattributes', for adding, and automatically
// removing, attributes from the current thread's attribute context.
//..
//  context->removeAttributes(it);
//..

#include <balscm_version.h>

#include <ball_attributecontainerlist.h>
#include <ball_ruleset.h>

#include <bslma_allocator.h>

#include <bslmt_threadutil.h>

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

#include <bsl_iosfwd.h>

namespace BloombergLP {
namespace ball {

class Attribute;
class AttributeContainer;
class Category;
class CategoryManager;
class ThresholdAggregate;

             // ==========================================
             // class AttributeContext_RuleEvaluationCache
             // ==========================================

class AttributeContext_RuleEvaluationCache {
    // This is an implementation type of 'AttributeContext' and should not be
    // used by clients of this package.  A rule evaluation cache is a mechanism
    // for evaluating and caching whether a rule is active.  A rule is
    // considered active if all of its attributes are satisfied by the
    // collection of attributes held in a 'AttributeContainerList' object
    // (i.e., 'Rule::evaluate' returns 'true' for the 'AttributeContainerList'
    // object).  The rules this cache evaluates are contained in a 'RuleSet'
    // object.  'RuleSet::MaskType' is a bit mask for a rule set, where each
    // bit is a boolean value associated with the rule at the corresponding
    // index in a rule set.  An 'AttributeContext' determines, using the
    // 'isDataAvailable' method, if a particular set of rules (described using
    // a bit mask) have already been evaluated.  A context accesses the current
    // cache of rule evaluations using the 'knownActiveRules' method.  Finally,
    // a context updates the cache of rule evaluations using the 'update'
    // method.  Note that the 'isDataAvailable' method should be used prior to
    // using 'knownActiveRules' in order to ensure the relevant rules have been
    // evaluated and that those evaluations are up-to-date.

    // DATA
    RuleSet::MaskType  d_evalMask;        // set of bits, each of which
                                          // indicates whether the
                                          // corresponding rule has been
                                          // evaluated and cached in
                                          // 'd_resultMask' (1 if evaluated and
                                          // 0 otherwise)

    RuleSet::MaskType  d_resultMask;      // set of bits, each of which caches
                                          // the result of the most recent
                                          // evaluation of the corresponding
                                          // rule (1 if the rule is active and
                                          // 0 otherwise)

    bsls::Types::Int64 d_sequenceNumber;  // sequence number used to determine
                                          // if this cache is in sync with the
                                          // rule set maintained by the
                                          // category manager (see 'update');
                                          // if the sequence number changes it
                                          // indicates the cache is out of date

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

  public:
    // CREATORS
    AttributeContext_RuleEvaluationCache();
        // Create an empty rule evaluation cache having a sequence number of
        // -1.

    // ~AttributeContext_RuleEvaluationCache();
        // Destroy this rule evaluation cache.  Note that this trivial
        // destructor is generated by the compiler.

    // MANIPULATORS
    void clear();
        // Clear any currently cached rule evaluation data, restoring this
        // object to its default constructed state (empty).

    RuleSet::MaskType update(bsls::Types::Int64            sequenceNumber,
                             RuleSet::MaskType             relevantRulesMask,
                             const RuleSet&                rules,
                             const AttributeContainerList& attributes);
        // Update, for the specified 'sequenceNumber', the cache for those
        // rules indicated by the specified 'relevantRulesMask' bit mask in the
        // specified set of 'rules', by evaluating those rules for the
        // specified 'attributes'; return the bit mask indicating those rules
        // that are known to be active.  If a bit in the returned bit mask
        // value is set to 1, the rule at the corresponding index in 'rules' is
        // "active"; however, if a bit is set to 0, the corresponding rule is
        // either not active *or* has not been evaluated.  This operation does,
        // however, guarantee that all the rules indicated by the
        // 'relevantRulesMask' *will* be evaluated.  A particular rule is
        // considered "active" if all of its attributes are satisfied by
        // 'attributes' (i.e., if 'Rule::evaluate' returns 'true' for
        // 'attributes').  The behavior is undefined unless 'rules' is not
        // modified during this operation (i.e., any lock associated with
        // 'rules' must be locked during this operation).

    // ACCESSORS
    bool isDataAvailable(bsls::Types::Int64 sequenceNumber,
                         RuleSet::MaskType  relevantRulesMask) const;
        // Return 'true' if this cache contains up-to-date cached rule
        // evaluations having the specified 'sequenceNumber' for the set of
        // rules indicated by the specified 'relevantRulesMask' bit mask, and
        // 'false' otherwise.

    RuleSet::MaskType knownActiveRules() const;
        // Return a bit mask indicating those rules, from the set of rules
        // provided in the last call to 'update', that are known to be active
        // (as of that last call to 'update').  If a bit in the returned value
        // is set to 1, the rule at the corresponding index is active; however,
        // if a bit is set to 0, the corresponding rule is either not active
        // *or* has not been evaluated.  Note that 'isDataAvailable' should be
        // called to test if this cache contains up-to-date evaluated rule
        // information for the rules in which they are interested before using
        // the result of this method.

    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
bsl::ostream& operator<<(
                     bsl::ostream&                               stream,
                     const AttributeContext_RuleEvaluationCache& cache);
    // Write a description of the data members of the specified 'cache' to the
    // specified 'stream' in some single-line human readable format, and return
    // the modifiable 'stream'.

                        // ======================
                        // class AttributeContext
                        // ======================

class AttributeContext {
    // This class provides a mechanism for associating attributes with the
    // current thread, and evaluating the logging rules associated with a
    // category using those stored attributes.  'AttributeContext' contains
    // class data members that must be initialized (using the 'initialize'
    // class method) with a 'CategoryManager' object containing a 'RuleSet'
    // that represents the currently installed logging rules for the process.
    // Clients can obtain the context for the current thread by calling the
    // 'getContext' class method.  The 'addAttributes' and 'removeAttributes'
    // methods are used to add and remove collections of attributes from the
    // (thread-local) context object.  Finally, 'AttributeContext' provides
    // methods (used primarily by other components in the 'ball' package') to
    // determine the effect of the current logging rules on the logging
    // thresholds of a category.  The 'hasRelevantActiveRules' method returns
    // 'true' if there are any relevant and active rules that might modify the
    // logging thresholds of the supplied category.  A rule is "relevant" if
    // the rule's pattern matches the category's name, and a rule is "active"
    // if all the attributes defined for the rule are satisfied by the current
    // thread's attributes (i.e., 'Rule::evaluate' returns 'true' for the
    // collection of attributes maintained for the current thread by the
    // thread's 'AttributeContext' object).  The 'determineThresholdLevels'
    // method returns the logging threshold levels for a category, factoring in
    // any active rules that apply to the category that might override the
    // category's thresholds.  The behavior for the 'hasRelevantActiveRules'
    // and 'determineThresholdLevels' methods is undefined unless 'initialize'
    // has been called.  Note that, in practice, 'initialize' is called
    // *internally* when the logger manager singleton is initialized; clients
    // ordinarily should not call 'initialize' directly.

    // PRIVATE TYPES
    typedef AttributeContext_RuleEvaluationCache RuleEvaluationCache;

    // CLASS DATA
    static CategoryManager  *s_categoryManager_p;  // holds the rule set, rule
                                                   // set sequence number, and
                                                   // rule set mutex

    static bslma::Allocator *s_globalAllocator_p;  // allocator for thread-
                                                   // local context objects

    // DATA
    AttributeContainerList   d_containerList;      // list of attribute
                                                   // containers

    mutable RuleEvaluationCache
                             d_ruleCache_p;        // cache of rule evaluations

    bslma::Allocator        *d_allocator_p;        // allocator used to create
                                                   // this object (held, not
                                                   // owned)

    // FRIENDS
    friend class AttributeContextProctor;

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

    // PRIVATE CLASS METHODS
    static const bslmt::ThreadUtil::Key& contextKey();
        // Return a 'const' reference to the singleton key for the thread-local
        // storage in which the 'AttributeContext' object is stored.  This
        // method creates the key on the first invocation; all subsequent
        // invocations return the key created on the initial call.  Note that
        // it is more efficient to cache the return value of this method than
        // to invoke it repeatedly.

    static void removeContext(void *arg);
        // Destroy the 'AttributeContext' object pointed to by the specified
        // 'arg'.  Note that this function is intended to be called by the
        // thread-specific storage facility when a thread exits.

    // PRIVATE CREATORS
    AttributeContext(bslma::Allocator *globalAllocator = 0);
        // Create an 'AttributeContext' object initially having no attributes.
        // Optionally specify a 'globalAllocator' used to supply memory.  If
        // 'globalAllocator' is 0, the currently installed global allocator is
        // used.  Note that the 'getContext' class method must be used to
        // obtain the address of the attribute context for the current thread.

    ~AttributeContext();
        // Destroy this object.

  public:
    // PUBLIC TYPES
    typedef AttributeContainerList::iterator iterator;

    // CLASS METHODS
    static AttributeContext *getContext();
        // Return the address of the current thread's attribute context, if
        // such context exists; otherwise, create an attribute context, install
        // it in thread-local storage, and return the address of the newly
        // created context.  Note that this method can be invoked safely even
        // if the 'initialize' class method has not yet been called.

    static void initialize(CategoryManager  *categoryManager,
                           bslma::Allocator *globalAllocator = 0);
        // Initialize the static data members of 'AttributeContext' using the
        // specified 'categoryManager'.  Optionally specify a 'globalAllocator'
        // used to supply memory.  If 'globalAllocator' is 0, the currently
        // installed global allocator is used.  Unless 'reset' is subsequently
        // called, invoking this method more than once will log an error
        // message using 'bsls::Log::platformDefaultMessageHandler', but will
        // have no other effect.  Note that in practice this method will be
        // called *automatically* when the 'LoggerManager' singleton is
        // initialized -- i.e., it is not intended to be called directly by
        // clients of the 'ball' package.

    static AttributeContext *lookupContext();
        // Return the address of the modifiable 'AttributeContext' object
        // installed in local storage for the current thread, or 0 if no
        // attribute context has been created for this thread.  Note that this
        // method can be invoked safely even if the 'initialize' class method
        // has not yet been called.

    static void reset();
        // Reset the static data members of 'AttributeContext' to their initial
        // state (0).  Unless 'initialize' is subsequently called, invoking
        // this method more than once has no effect.  Note that in practice
        // this method will be called *automatically* when the 'LoggerManager'
        // singleton is destroyed -- i.e., it is not intended to be called
        // directly by clients of the 'ball' package.

    static void visitAttributes(
                   const bsl::function<void(const ball::Attribute&)>& visitor);
        // Invoke the specified 'visitor' for all attributes in all attribute
        // containers maintained by this object.

    // MANIPULATORS
    iterator addAttributes(const AttributeContainer *attributes);
        // Add the specified 'attributes' container to the list of attribute
        // containers maintained by this object.  The behavior is undefined
        // unless 'attributes' remains valid *and* *unmodified* until either
        // 'attributes' is removed from this context, 'clearCache' is called,
        // or this object is destroyed.  Note that this method can be invoked
        // safely even if the 'initialize' class method has not yet been
        // called.

    void clearCache();
        // Clear this object's cache of evaluated rules.  Note that this method
        // must be called if an 'AttributeContainer' object supplied to
        // 'addAttributes' is modified outside of this context.

    void removeAttributes(iterator element);
        // Remove the specified 'element' from the list of attribute containers
        // maintained by this object.  Note that this method can be invoked
        // safely even if the 'initialize' class method has not yet been
        // called.

    // ACCESSORS
    bool hasRelevantActiveRules(const Category *category) const;
        // Return 'true' if there is at least one rule defined for this process
        // that is both "relevant" to the specified 'category' and "active",
        // and 'false' otherwise.  A rule is "relevant" to 'category' if the
        // rule's pattern matches 'category->categoryName()', and a rule is
        // "active" if all the attributes defined for that rule are satisfied
        // by the current thread's attributes (i.e., 'Rule::evaluate' returns
        // 'true' for the collection of attributes maintained by this object).
        // This method operates on the set of rules maintained by the category
        // manager supplied to the 'initialize' class method (which, in
        // practice, should be the global set of rules for the process).  The
        // behavior is undefined unless 'initialize' has previously been
        // invoked without a subsequent call to 'reset', and 'category' is
        // contained in the registry maintained by the category manager
        // supplied to 'initialize'.

    void determineThresholdLevels(ThresholdAggregate *levels,
                                  const Category     *category) const;
        // Populate the specified 'levels' with the threshold levels for the
        // specified 'category'.  This method compares the threshold levels
        // defined by 'category' with those of any active rules that apply to
        // that category, and determines the minimum severity (i.e., the
        // maximum numerical value) for each respective threshold amongst those
        // values.  A rule applies to 'category' if the rule's pattern matches
        // 'category->categoryName()', and a rule is active if all the
        // attributes defined for that rule are satisfied by the current
        // thread's attributes (i.e., 'Rule::evaluate' returns 'true' for the
        // collection of attributes maintained by this object).  This method
        // operates on the set of rules maintained by the category manager
        // supplied to the 'initialize' class method (which, in practice,
        // should be the global set of rules for the process).  The behavior is
        // undefined unless 'initialize' has previously been invoked without a
        // subsequent call to 'reset', and 'category' is contained in the
        // registry maintained by the category manager supplied to
        // 'initialize'.

    bool hasAttribute(const Attribute& value) const;
        // Return 'true' if an attribute having the specified 'value' exists in
        // any of the attribute containers maintained by this object, and
        // 'false' otherwise.  Note that this method can be invoked safely even
        // if the 'initialize' class method has not yet been called.

    const AttributeContainerList& containers() const;
        // Return a 'const' reference to the list of attribute containers
        // maintained by this object.  Note that this method can be invoked
        // safely even if the 'initialize' class method has not yet been
        // called.

    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
bsl::ostream& operator<<(bsl::ostream&           stream,
                         const AttributeContext& context);
    // Write a description of the data members of the specified 'context' to
    // the specified 'stream' in a single-line human readable format, and
    // return a reference to the modifiable 'stream'.

                     // =============================
                     // class AttributeContextProctor
                     // =============================

class AttributeContextProctor {
    // This class implements a proctor that, on its own destruction, will
    // destroy the attribute context of the current thread.  Attribute contexts
    // are stored in thread-local memory.  On destruction, objects of this type
    // will deallocate the current thread's attribute context (if one has been
    // created), and set the thread-local storage pointer to 0.

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

  public:
    // CREATORS
    explicit AttributeContextProctor();
        // Create an 'AttributeContextProctor' object that will destroy the
        // current attribute context on destruction.

    ~AttributeContextProctor();
        // Destroy this object (as well as the current attribute context).
};

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

               // ------------------------------------------
               // class AttributeContext_RuleEvaluationCache
               // ------------------------------------------

// CREATORS
inline
AttributeContext_RuleEvaluationCache::AttributeContext_RuleEvaluationCache()
: d_evalMask(0)
, d_resultMask(0)
, d_sequenceNumber(-1)
{
}

// MANIPULATORS
inline
void AttributeContext_RuleEvaluationCache::clear()
{
    d_evalMask       =  0;
    d_resultMask     =  0;
    d_sequenceNumber = -1;
}

// ACCESSORS
inline
bool AttributeContext_RuleEvaluationCache::isDataAvailable(
                                    bsls::Types::Int64 sequenceNumber,
                                    RuleSet::MaskType  relevantRulesMask) const
{
    return sequenceNumber    == d_sequenceNumber
        && relevantRulesMask == (relevantRulesMask & d_evalMask);
}

inline
RuleSet::MaskType
AttributeContext_RuleEvaluationCache::knownActiveRules() const
{
    return d_resultMask;
}

                        // ----------------------
                        // class AttributeContext
                        // ----------------------

// MANIPULATORS
inline
AttributeContext::iterator
AttributeContext::addAttributes(const AttributeContainer *attributes)
{
    BSLS_ASSERT(attributes);

    d_ruleCache_p.clear();
    return d_containerList.pushFront(attributes);
}

inline
void AttributeContext::clearCache()
{
    d_ruleCache_p.clear();
}

inline
void AttributeContext::removeAttributes(iterator element)
{
    d_ruleCache_p.clear();
    d_containerList.remove(element);
}

// ACCESSORS
inline
const AttributeContainerList& AttributeContext::containers() const
{
    return d_containerList;
}

inline
bool AttributeContext::hasAttribute(const Attribute& value) const
{
    return d_containerList.hasValue(value);
}

                        // -----------------------------
                        // class AttributeContextProctor
                        // -----------------------------

// CREATORS
inline
AttributeContextProctor::AttributeContextProctor()
{
}

}  // close package namespace

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

inline
bsl::ostream& ball::operator<<(bsl::ostream&           stream,
                               const AttributeContext& context)
{
    return context.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 ----------------------------------