// ball_rule.h                                                        -*-C++-*-
#ifndef INCLUDED_BALL_RULE
#define INCLUDED_BALL_RULE

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

//@PURPOSE: Provide an object having a pattern, thresholds, and attributes.
//
//@CLASSES:
//  ball::Rule: a pattern, thresholds, and attribute sets
//
//@SEE_ALSO: ball_ruleset
//
//@DESCRIPTION: This component implements a type, 'ball::Rule', that consists
// of a pattern, four threshold levels, and a set of attributes.  The pattern
// indicates the names of the categories for which the rule will become
// relevant.  The four threshold levels determine what actions will be
// performed on log records when their severity level equals or exceeds any of
// these threshold levels.  The attribute set is a collection of unique
// attribute name/value pairs.
//
// Note that multiple attributes with the same name are permitted so long as
// they correspond to different values.
//
// 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
///-----
// The following code fragments illustrate how to create a rule and add
// attributes.
//
// We create a rule whose pattern is 'WEEKEND*' and whose threshold levels are
// all 'ball::Severity::e_OFF' except the 'pass-through' level.  A
// 'pass-through' level of 'ball::Severity::e_INFO' indicates that whenever the
// rule is active and the severity is equal to or exceeds
// 'ball::Severity::e_INFO', log records will be passed to the observer:
//..
//  ball::Rule rule("WEEKEND*",              // pattern
//                  ball::Severity::e_OFF,   // record level
//                  ball::Severity::e_INFO,  // pass-through level
//                  ball::Severity::e_OFF,   // trigger level
//                  ball::Severity::e_OFF);  // triggerAll level
//..
// Create some attributes and then add one to the rule:
//..
//  ball::ManagedAttribute p1("myLib.uuid", 4044457);
//  ball::ManagedAttribute p2("myLib.name", "John Smith");
//  rule.addAttribute(p1);
//..
// Attributes can be looked up by the 'hasAttribute' method:
//..
//  assert(true  == rule.hasAttribute(p1));
//  assert(false == rule.hasAttribute(p2));
//..
// We then add the other attribute:
//..
//  rule.addAttribute(p2);
//  assert(true  == rule.hasAttribute(p2));
//..
// Attributes can also be removed from the rule by the 'removeAttribute'
// method:
//..
//  rule.removeAttribute(p1);
//  assert(false == rule.hasAttribute(p1));
//  assert(true  == rule.hasAttribute(p2));
//..
// The pattern of a rule can be changed by the 'setPattern' method:
//..
//  assert(0 == strcmp(rule.pattern(), "WEEKEND*"));
//
//  rule.setPattern("WEEKDAY*");
//  assert(0 == strcmp(rule.pattern(), "WEEKDAY*"));
//..
// The threshold levels of a rule can also be modified by the 'setLevels'
// method:
//..
//  assert(ball::Severity::e_OFF  == rule.recordLevel());
//  assert(ball::Severity::e_INFO == rule.passLevel());
//  assert(ball::Severity::e_OFF  == rule.triggerLevel());
//  assert(ball::Severity::e_OFF  == rule.triggerAllLevel());
//
//  rule.setLevels(ball::Severity::e_INFO,
//                 ball::Severity::e_OFF,
//                 ball::Severity::e_INFO,
//                 ball::Severity::e_INFO);
//
//  assert(ball::Severity::e_INFO == rule.recordLevel());
//  assert(ball::Severity::e_OFF  == rule.passLevel());
//  assert(ball::Severity::e_INFO == rule.triggerLevel());
//  assert(ball::Severity::e_INFO == rule.triggerAllLevel());
//..

#include <balscm_version.h>

#include <ball_managedattribute.h>
#include <ball_managedattributeset.h>
#include <ball_patternutil.h>
#include <ball_predicate.h>
#include <ball_predicateset.h>
#include <ball_thresholdaggregate.h>

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

#include <bslmf_nestedtraitdeclaration.h>

#include <bsl_string.h>

namespace BloombergLP {
namespace ball {

class AttributeContainerList;

                        // ==========
                        // class Rule
                        // ==========

class Rule {
    // This class defines a value-semantic object that holds a pattern, four
    // threshold levels, and an attribute set.  For each of these fields there
    // is an accessor for obtaining the field value and a manipulator for
    // changing that value.  There are a few methods as well for directly
    // adding/removing/searching attributes.
    //
    // Additionally, this class supports a complete set of *value* *semantic*
    // operations, including copy construction, assignment and equality
    // comparison, and 'ostream' printing.  A precise operational definition of
    // when two instances have the same value can be found in the description
    // of 'operator==' for the class.  This class is *exception* *neutral* with
    // no guarantee of rollback: If an exception is thrown during the
    // invocation of a method on a pre-existing instance, the object is left in
    // a valid state, but its value is undefined.  In no event is memory
    // leaked.  Finally, *aliasing* (e.g., using all or part of an object as
    // both source and destination) is supported in all cases.

    // DATA
    bsl::string         d_pattern;       // the pattern for the name of
                                         // categories to which this rule will
                                         // become relevant

    ThresholdAggregate  d_thresholds;    // an aggregate of four threshold
                                         // levels

    ManagedAttributeSet d_attributeSet;  // set of attributes

    mutable int         d_hashValue;     // cached hash value; < 0 indicates it
                                         // is invalid

    mutable int         d_hashSize;      // number of slots from which
                                         // 'd_hashValue' was calculated; 0
                                         // indicates it is invalid

    // FRIENDS
    friend bool operator==(const Rule&, const Rule&);
    friend bool operator!=(const Rule&, const Rule&);
    friend bsl::ostream& operator<<(bsl::ostream&, const Rule&);

  public:
    // CLASS METHODS
    static int hash(const Rule& rule, int size);
        // Return a hash value calculated from the specified 'rule' using the
        // specified 'size' as the number of slots.  The value returned is
        // guaranteed to be in the range '[0 .. size - 1]'.  The behavior is
        // undefined unless '0 < size'.

    // TRAITS
    BSLMF_NESTED_TRAIT_DECLARATION(Rule, bslma::UsesBslmaAllocator);

    // CREATORS
    explicit Rule(bslma::Allocator *basicAllocator = 0);
        // Create a 'Rule' object whose pattern is an empty string and whose
        // thresholds levels are all 0.  Optionally specify a 'basicAllocator'
        // used to supply memory.  If 'basicAllocator' is 0, the currently
        // installed default allocator will be used.  Note that a newly created
        // 'Rule' object does not have any attributes.

    Rule(const bsl::string_view&  pattern,
         int                      recordLevel,
         int                      passLevel,
         int                      triggerLevel,
         int                      triggerAllLevel,
         bslma::Allocator        *basicAllocator = 0);
        // Create a 'Rule' object whose pattern is the specified 'pattern' and
        // whose thresholds levels are the specified 'recordLevel',
        // 'passLevel', 'triggerLevel', and 'triggerAllLevel' respectively.
        // Optionally specify a 'basicAllocator' used to supply memory.  If
        // 'basicAllocator' is 0, the currently installed default allocator
        // will be used.  The behavior is undefined unless each of the four
        // threshold level values is not in the range [0 .. 255].  Note that a
        // newly created 'Rule' object does not have any attributes.

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

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

    // MANIPULATORS
    Rule& operator=(const Rule& rhs);
        // Assign to this object the value of the specified 'rhs' object.

    int addAttribute(const ManagedAttribute& value);
        // Add an attribute having the specified 'value' to this object.
        // Return 1 on success and 0 if an attribute having the same value
        // already exists in this object.

    int addPredicate(const ManagedAttribute& value);
        // !DEPRECATED!: Use 'addAttribute' instead.

    int removeAttribute(const ManagedAttribute& value);
        // Remove the attribute having the specified 'value' from this object.
        // Return the number of attributes being removed (i.e., 1 on success
        // and 0 if no attribute having 'value' exists in this object).

    int removePredicate(const ManagedAttribute& value);
        // !DEPRECATED!: Use 'removeAttribute' instead.

    void removeAll();
        // Remove all attributes from this rule.

    void removeAllPredicates();
        // !DEPRECATED!: Use 'removeAll' instead.

    int setLevels(int recordLevel,
                  int passLevel,
                  int triggerLevel,
                  int triggerAllLevel);
        // Set the threshold levels of this object to the specified
        // 'recordLevel', 'passLevel', 'triggerLevel', and 'triggerAllLevel'
        // values, respectively, if each of the specified values is in the
        // range [0 .. 255].  Return 0 on success, and a non-zero value
        // otherwise (with no effect on the threshold levels of this object).

    void setPattern(const bsl::string_view& value);
        // Set the pattern of this object to the specified 'value'.

    // ACCESSORS
    bool evaluate(const AttributeContainerList& containerList) const;
        // Return 'true' if for every attribute maintained by this object, an
        // attribute with the same name and value exists in the specified
        // 'containerList'; and 'false' otherwise.

    int numAttributes() const;
        // Return the number of attributes managed by this object.

    int numPredicates() const;
        // !DEPRECATED!: Use 'numAttributes' instead.

    bool hasAttribute(const ManagedAttribute& value) const;
        // Return 'true' if an attribute having specified 'value' exists in
        // this object, and 'false' otherwise.

    bool hasPredicate(const Predicate& value) const;
        // !DEPRECATED!: Use 'hasAttribute' instead.

    ManagedAttributeSet::const_iterator begin() const;
        // Return an iterator referring to the first member of the attribute
        // set maintained by this object.

    ManagedAttributeSet::const_iterator end() const;
        // Return an iterator referring to one past the last member of the
        // attribute set maintained by this object.

    int recordLevel() const;
        // Return the record level of this object.

    int passLevel() const;
        // Return the pass level of this object.

    int triggerLevel() const;
        // Return the trigger level of this object.

    int triggerAllLevel() const;
        // Return the trigger-all level of this object.

    const char *pattern() const;
        // Return the pattern of this object.

    bool isMatch(const char *inputString) const;
        // Return 'true' if the specified 'inputString' matches the pattern of
        // this rule, and 'false' otherwise.  (For the definition of a string
        // matching the pattern of a rule, please refer to the function-level
        // documentation associated with the 'PatternUtil::isMatch' function).

    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 Rule& lhs, const Rule& rhs);
    // Return 'true' if the specified 'lhs' and 'rhs' objects have the same
    // value, and 'false' otherwise.  Two 'Rule' objects have the same value if
    // they have the same attributes, the same four respective threshold
    // levels, and the same pattern.

bool operator!=(const Rule& lhs, const Rule& rhs);
    // Return 'true' if the specified 'lhs' and 'rhs' objects do not have the
    // same value, and 'false' otherwise.  Two 'Rule' objects do not have the
    // same value if they have different attributes, different values for any
    // of the four respective threshold levels, or different patterns.

bsl::ostream& operator<<(bsl::ostream& output, const Rule& rule);
    // Write the value of the specified 'rule' to the specified 'output'
    // stream.  Return the specified 'output' stream.

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

                        // ----------
                        // class Rule
                        // ----------

// CREATORS
inline
Rule::Rule(bslma::Allocator *basicAllocator)
: d_pattern("", basicAllocator)
, d_thresholds(0, 0, 0, 0)
, d_attributeSet(basicAllocator)
, d_hashValue(-1)
, d_hashSize(0)
{
}

inline
Rule::Rule(const bsl::string_view&   pattern,
           int                       recordLevel,
           int                       passLevel,
           int                       triggerLevel,
           int                       triggerAllLevel,
           bslma::Allocator         *basicAllocator)
: d_pattern(pattern.data(), pattern.length(), basicAllocator)
, d_thresholds(recordLevel, passLevel, triggerLevel, triggerAllLevel)
, d_attributeSet(basicAllocator)
, d_hashValue(-1)
, d_hashSize(0)
{
}

inline
Rule::Rule(const Rule& original, bslma::Allocator *basicAllocator)
: d_pattern(original.d_pattern, basicAllocator)
, d_thresholds(original.d_thresholds)
, d_attributeSet(original.d_attributeSet, basicAllocator)
, d_hashValue(original.d_hashValue)
, d_hashSize(original.d_hashSize)
{
}

// MANIPULATORS
inline
int Rule::addAttribute(const ManagedAttribute& value)
{
    d_hashValue = -1;
    return d_attributeSet.addAttribute(value);
}

inline
int Rule::addPredicate(const ManagedAttribute& value)
{
    return addAttribute(value);
}

inline
int Rule::removeAttribute(const ManagedAttribute& value)
{
    d_hashValue = -1;
    return d_attributeSet.removeAttribute(value);
}

inline
int Rule::removePredicate(const ManagedAttribute& value)
{
    return removeAttribute(value);
}

inline
void Rule::removeAll()
{
    d_hashValue = -1;
    d_attributeSet.removeAll();
}

inline
void Rule::removeAllPredicates()
{
    removeAll();
}

inline
int Rule::setLevels(int recordLevel,
                    int passLevel,
                    int triggerLevel,
                    int triggerAllLevel)
{
    d_hashValue = -1;
    return d_thresholds.setLevels(recordLevel,
                                  passLevel,
                                  triggerLevel,
                                  triggerAllLevel);
}

inline
void Rule::setPattern(const bsl::string_view& value)
{
    d_hashValue = -1;
    d_pattern.assign(value);
}

// ACCESSORS
inline
bool Rule::evaluate(const AttributeContainerList& containerList) const
{
    return d_attributeSet.evaluate(containerList);
}

inline
int Rule::numAttributes() const
{
    return d_attributeSet.numAttributes();
}

inline
int Rule::numPredicates() const
{
    return numAttributes();
}

inline
bool Rule::hasAttribute(const ManagedAttribute& value) const
{
    return d_attributeSet.isMember(value);
}

inline
bool Rule::hasPredicate(const ManagedAttribute& value) const
{
    return hasAttribute(value);
}

inline
ManagedAttributeSet::const_iterator Rule::begin() const
{
    return d_attributeSet.begin();
}

inline
ManagedAttributeSet::const_iterator Rule::end() const
{
    return d_attributeSet.end();
}

inline
int Rule::recordLevel() const
{
    return d_thresholds.recordLevel();
}

inline
int Rule::passLevel() const
{
    return d_thresholds.passLevel();
}

inline
int Rule::triggerLevel() const
{
    return d_thresholds.triggerLevel();
}

inline
int Rule::triggerAllLevel() const
{
    return d_thresholds.triggerAllLevel();
}

inline
const char *Rule::pattern() const
{
    return d_pattern.c_str();
}

inline
bool Rule::isMatch(const char *inputString) const
{
    return PatternUtil::isMatch(inputString, d_pattern.c_str());
}

}  // close package namespace

// FREE OPERATORS
inline
bool ball::operator==(const Rule& lhs, const Rule& rhs)
{
    if (lhs.d_hashValue > 0
     && rhs.d_hashValue > 0
     && lhs.d_hashSize  == rhs.d_hashSize
     && lhs.d_hashValue != rhs.d_hashValue) {
        return false;                                                 // RETURN
    }

    return lhs.d_pattern      == rhs.d_pattern
        && lhs.d_thresholds   == rhs.d_thresholds
        && lhs.d_attributeSet == rhs.d_attributeSet;
}

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

inline
bsl::ostream& ball::operator<<(bsl::ostream& output, const Rule& rule)
{
    return rule.print(output, 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 ----------------------------------