// ball_ruleset.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_RULESET
#define INCLUDED_BALL_RULESET

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

//@PURPOSE: Provide a set of unique rules.
//
//@CLASSES:
//  ball::RuleSet: container for unique rules
//
//@SEE_ALSO: ball_rule
//
//@DESCRIPTION: This component provides a value-semantic container,
// 'ball::RuleSet', for storage and efficient retrieval of 'ball::Rule'
// objects.
//
// 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".
//
///Thread Safety
///-------------
// 'ball::RuleSet' is *not* thread-safe in that multiple threads attempting to
// concurrently modify the same 'ball::RuleSet' object will leave the object in
// an undefined state.  To ensure thread-safety, concurrent accesses to a
// 'ball::RuleSet' must be serialized by a mutex.
//
///Usage
///-----
// The following code fragments illustrate how to use a rule set.
//
// We first 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 equals or exceeds 'ball::Severity::e_INFO',
// log records will be passed to the observer:
//..
//  ball::Rule rule1("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
//..
// Next, we create another rule having a different pattern, but the same
// threshold levels:
//..
//  ball::Rule rule2("WEEKDAY*",               // 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
//..
// We then create a 'ball::RuleSet' object, add the two rules, and verify that
// rules were added correctly:
//..
//  ball::RuleSet ruleSet;
//  assert(0 <= ruleSet.addRule(rule1));
//  assert(0 <= ruleSet.addRule(rule2));
//  assert(2 == ruleSet.numRules());
//..
// Duplicate rules cannot be added:
//..
//  assert(-1 == ruleSet.addRule(rule1));
//  assert(-1 == ruleSet.addRule(rule2));
//  assert( 2 == ruleSet.numRules());
//..
// Rules in a rule set can be looked up by the 'ruleId' method:
//..
//  int i1 = ruleSet.ruleId(rule1);
//  int i2 = ruleSet.ruleId(rule2);
//  assert(0 <= i1); assert(i1 < ruleSet.maxNumRules());
//  assert(0 <= i2); assert(i2 < ruleSet.maxNumRules());
//  assert(i1 != i2);
//..
// The 'removeRule' method can be used to remove rules from a rule set.
//..
//  assert(ruleSet.removeRule(rule1));
//  assert(1 == ruleSet.numRules());
//  assert(ruleSet.ruleId(rule1) < 0);
//  assert(ruleSet.ruleId(rule2) == i2);
//..

#include <balscm_version.h>

#include <ball_rule.h>

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

#include <bslmf_nestedtraitdeclaration.h>

#include <bsl_unordered_set.h>
#include <bsl_vector.h>

namespace BloombergLP {
namespace ball {

                          // =============
                          // class RuleSet
                          // =============

class RuleSet {
    // This class manages a set of unique rule values.  Rules may be added to
    // or removed from the set; however, rules having duplicate values will
    // not be added.  For the definition of two rules having the same value,
    // please refer to the function-level documentation associated with the
    // 'Rule::operator==' function.

  public:
    // PUBLIC TYPES
    typedef unsigned int MaskType;
        // 'MaskType' is an alias for the fundamental integral type used to
        // indicate rule subsets compactly.

    enum {
        e_MAX_NUM_RULES = 8 * sizeof(MaskType)
           // The maximum number of rules managed by this object.

#ifndef BDE_OMIT_INTERNAL_DEPRECATED
      , BAEL_MAX_NUM_RULES = e_MAX_NUM_RULES
      , MAX_NUM_RULES      = e_MAX_NUM_RULES
#endif // BDE_OMIT_INTERNAL_DEPRECATED
    };

  private:
    // PRIVATE TYPES
    struct RuleHash {
        // hash functor for 'Rule'

      private:
        // CLASS DATA
        static int s_hashtableSize;              // the default hashtable size

      public:
        // ACCESSORS
        int operator()(const Rule& rule) const
            // Return the hash value of the specified 'rule'.
        {
            return Rule::hash(rule, s_hashtableSize);
        }
    };

    typedef bsl::unordered_set<Rule, RuleHash> HashtableType;

    // DATA
    HashtableType              d_ruleHashtable;  // the hash table that
                                                 // manages all the rules
                                                 // maintained by this rule set

    bsl::vector<const Rule *>  d_ruleAddresses;  // secondary map between ids
                                                 // and the addresses of rules

    bsl::vector<int>           d_freeRuleIds;    // rule Ids that are not being
                                                 // used

    int                        d_numPredicates;  // total number of predicates

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

  public:
    // CLASS METHODS
    static int maxNumRules();
        // Return the maximum number of rules that can be simultaneously
        // maintained by this object.

    static void printMask(bsl::ostream& stream,
                          MaskType      mask,
                          int           level = 0,
                          int           spacesPerLevel = 0);
        // Format the specified 'mask' to the specified output 'stream' at the
        // optionally specified indentation 'level' and return a reference to
        // the modifiable 'stream'.  If 'level' is specified, optionally
        // specify 'spacesPerLevel', the number of spaces per indentation level
        // for this and all of its nested objects.  Each line is indented by
        // the absolute value of 'level * spacesPerLevel'.  If 'level' is
        // negative, suppress indentation of the first line.  If
        // 'spacesPerLevel' is negative, suppress line breaks and format the
        // entire output on one line.  If 'stream' is initially invalid, this
        // operation has no effect.

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

    // CREATORS
    explicit RuleSet(bslma::Allocator *basicAllocator = 0);
        // Create an empty rule set.  Optionally specify a 'basicAllocator'
        // used to supply memory.  If 'basicAllocator' is 0, the currently
        // installed default allocator will be used.

    RuleSet(const RuleSet& original, bslma::Allocator *basicAllocator = 0);
        // Create a 'RuleSet' object having 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.

    //! ~RuleSet() = default;
        // Destroy this rule set.

    // MANIPULATOR
    int addRule(const Rule& value);
        // Create a new 'Rule' object having the specified 'value'.  Return the
        // non-negative id of this non-modifiable object on success, and a
        // negative value otherwise.  A return value of -1 indicates that
        // another rule having this value already exists.  A return value of -2
        // indicates that the maximum number of rules for this rule set has
        // been reached.

    int addRules(const RuleSet& rules);
        // Add each rule in the specified 'rules' to this rule set.  Return
        // the number of rules added.  Note that a rule in 'rules' will be
        // ignored if there is an existing rule having the same value or if
        // the number of rules in the set has reached the upper limit.  Also
        // note that if not all valid rules will fit, the (possibly empty)
        // subset of unique values that will be added is implementation
        // dependent.

    int removeRuleById(int id);
        // Remove from this rule set the rule having the specified 'id'.
        // Return the number of rules removed (i.e., 1 on success and 0 if
        // there is no rule whose id is 'id').  The behavior is undefined
        // unless '0 <= id < e_MAX_NUM_RULES'.

    int removeRule(const Rule& value);
        // Remove the rule having the specified 'value' from this rule set.
        // Return the number of rules removed (i.e., 1 on success and 0 if
        // there is no such a rule).

    int removeRules(const RuleSet& rules);
        // Remove each rule in the specified 'rules' from this rule set.
        // Return the number of rules removed.

    void removeAllRules();
        // Remove every rule in the rule set maintained by this object.

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

    // ACCESSORS
    int ruleId(const Rule& value) const;
        // Return the id of the rule having the specified 'value' if such a
        // rule exists, and a negative value otherwise.  Note that if there are
        // multiple rules having 'value', the id of the first one found will be
        // returned and the order in which rules are searched is implementation
        // dependent.

    const Rule *getRuleById(int id) const;
        // Return the address of the rule having the specified 'id' if such a
        // rule exists, and 0 otherwise.  The behavior is undefined unless
        // '0 <= id < maxNumRules()'.  Note that rules may be assigned
        // non-sequential identifiers, and that there may be a valid rule whose
        // identifier is greater than 'numRules()' (i.e., valid rules may
        // appear anywhere in the range '0 <= id < maxNumRules()').

    int numRules() const;
        // Return the number of unique rules maintained in this 'RuleSet'
        // object.  Note that this value is *not* the maximum identifier for
        // the rules currently in this container.

    int numPredicates() const;
        // Return the total number of predicates in all rules maintained by
        // this object.

    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 RuleSet& lhs, const RuleSet& rhs);
    // Return 'true' if the specified 'lhs' and 'rhs' rule sets have the same
    // value, and 'false' otherwise.  Two rule sets have the same value if
    // every rule that exists in one rule set also exists in the other.

bool operator!=(const RuleSet& lhs, const RuleSet& rhs);
    // Return 'true' if the specified 'lhs' and 'rhs' rule sets do not have the
    // same value, and 'false' otherwise.  Two rule sets do not have the same
    // value if there is at least one rule that exists in one rule but does not
    // exist in the other.

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

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

                          // -------------
                          // class RuleSet
                          // -------------

// CLASS METHODS
inline
int RuleSet::maxNumRules()
{
    return e_MAX_NUM_RULES;
}

// ACCESSORS
inline
const Rule *RuleSet::getRuleById(int id) const
{
    return d_ruleAddresses[id];
}

inline
int RuleSet::numRules() const
{
    return static_cast<int>(d_ruleHashtable.size());
}

inline
int RuleSet::numPredicates() const
{
    return d_numPredicates;
}

}  // close package namespace

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