Logo Search packages:      
Sourcecode: tagcoll version File versions  Download package

Expression.h

#ifndef TAGCOLL_EXPRESSION_H
#define TAGCOLL_EXPRESSION_H

/*
 * Expression that can match tagsets
 * 
 * Copyright (C) 2003,2004,2005  Enrico Zini <enrico@debian.org>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
 */

#include <tagcoll/Filter.h>
#include <string>
#include <map>

namespace Tagcoll
{

class TagexprContext;

/**
 * Interface for parsed tag expressions
 */
00036 class ExpressionImpl
{
protected:
      int _ref;

public:
      ExpressionImpl() : _ref(0) {}
      virtual ~ExpressionImpl() {}

      /// Increment the reference count for this object
00046       void ref() throw () { ++_ref; }

      /// Decrement the reference count for this object, returning true when it
      /// reaches 0
00050       bool unref() throw () { return --_ref == 0; }

      /**
       * Provide a string representation of this expression
       */
      virtual std::string format() const = 0;

      /**
       * Evaluates the expression on a recursive context
       *
       * \see TagexprContext
       */
      virtual bool eval(const TagexprContext& context) const = 0;

      /**
       * Evaluates the expression on a set of tags
       *
       * \return
       *   true if the expression matches the tags, false otherwise
       */
      virtual bool eval(const OpSet<std::string>& tags) const = 0;

      /**
       * Return a clone of this tag expression
       */
      //virtual Tagexpr* clone() const = 0;
};

class Expression
{
protected:
      ExpressionImpl* m_impl;

      Expression(ExpressionImpl* impl) : m_impl(impl) { m_impl->ref(); }

      const ExpressionImpl* impl() const { return m_impl; }
      ExpressionImpl* impl() { return m_impl; }

public:
      Expression();
      Expression(const std::string& expr);

      Expression(const Expression& e)
      {
            if (e.m_impl)
                  e.m_impl->ref();
            m_impl = e.m_impl;
      }
      ~Expression() { if (m_impl->unref()) delete m_impl; }

      Expression& operator=(const Expression& e)
      {
            if (e.m_impl)
                  e.m_impl->ref();  // Do it early to correctly handle the case of x = x;
            if (m_impl && m_impl->unref())
                  delete m_impl;
            m_impl = e.m_impl;
            return *this;
      }

      Expression operator and (const Expression& e);
      Expression operator or (const Expression& e);
      Expression operator not ();

      template<typename M>
      bool operator()(const OpSet<M>& tags) const { return m_impl->eval(tags); }

      bool operator()(const TagexprContext& context) const { return m_impl->eval(context); }

      std::string format() const { return m_impl->format(); }

      static Expression matchTag(const std::string& pattern);
};

/**
 * Context for evaluating expressions of derived tags.
 *
 * A derived tag is a tag which is automatically inferred when a tag expression
 * is matched on a tagset.
 *
 * TagexprContext allows the inference engine to distinguish between a normal
 * tag or a derived tag.
 *
 * This class is mainly used to support DerivedTags and has probably little
 * applications elsewhere.
 */
00136 class TagexprContext
{
protected:
      const OpSet<std::string>& tags;
      const std::map<std::string, Expression>& derivedTags;
      // Tags "visited" during tag evaluation: used to break circular loops
      mutable OpSet<std::string> seen;

public:
      /**
       * Create a context for recursive tagset evaluation
       *
       * Evaluation happens using a derivation table, which can list a tag as an
       * alias for another tag expression.  Whenever a tag is matched for
       * equality with a derived tag, the match is performed with the derived tag
       * expression instead.
       * 
       * \param tags
       *   The tags to evaluate
       * \param derivedTags
       *   The table of derived tags to use in the evaluation
       */
00158       TagexprContext(const OpSet<std::string>& tags, const std::map<std::string, Expression>& derivedTags)
            : tags(tags), derivedTags(derivedTags) {}

      /**
       * Evaluates the input tags on the contents to see if they contain the
       * given tag, or if they match its associated tag expression if tag is a
       * derived tag
       */
      bool eval(const std::string& tag) const;
};


/**
 * Remove the items that do not match a tag expression.
 */
template<class ITEM, class TAG>
00174 class FilterItemsByExpression : public Filter<ITEM, TAG>
{
public:
      enum MatchType { PLAIN, INVERTED };

protected:
      Expression expr;
      MatchType matchType;
      int matched;

      bool match(const OpSet<TAG>& tags) const
      {
            if (matchType == PLAIN)
                  return expr(tags);
            else
                  return !expr(tags);

      }

00193       virtual void consumeItemUntagged(const ITEM& item)
      {
            if (match(OpSet<TAG>()))
            {
                  matched++;
                  this->consumer->consume(item);
            }
      }
00201       virtual void consumeItem(const ITEM& item, const OpSet<TAG>& tags)
      {
            if (match(tags))
            {
                  matched++;
                  this->consumer->consume(item, tags);
            }
      }
00209       virtual void consumeItemsUntagged(const OpSet<ITEM>& items)
      {
            if (match(OpSet<TAG>()))
            {
                  matched += items.size();
                  this->consumer->consume(items);
            }
      }
00217       virtual void consumeItems(const OpSet<ITEM>& items, const OpSet<TAG>& tags)
      {
            if (match(tags))
            {
                  matched += items.size();
                  this->consumer->consume(items, tags);
            }
      }
      
public:     
      FilterItemsByExpression(const Expression& expression) :
            expr(expression), matchType(PLAIN), matched(0) {}
      FilterItemsByExpression(const std::string& expression) :
            expr(expression), matchType(PLAIN), matched(0) {}
      FilterItemsByExpression(Consumer<ITEM, TAG>&cons, const Expression& expression) :
            Filter<ITEM, TAG>(cons),
            expr(expression), matchType(PLAIN), matched(0) {}
      FilterItemsByExpression(Consumer<ITEM, TAG>&cons, const std::string& expression) :
            Filter<ITEM, TAG>(cons),
            expr(expression), matchType(PLAIN), matched(0) {}
      virtual ~FilterItemsByExpression() {}

      /**
       * Set the expression to use for this filter
       *
       * @param expression
       *   The expression to use for matching
       */
00245       void setExpression(const Expression& expression)
      {
            expr = expression;
      }

      /**
       * Set the expression to use for this filter
       *
       * @param expression
       *   The expression to use for matching
       */
00256       void setExpression(const std::string& expression)
      {
            expr = Expression(expression);
      }

      /**
       * Set the type of match
       *
       * @param type
       *   PLAIN: only keep the items that match the expression
       *   INVERTED: only keep the items that do not match the expression
       */
00268       void setMatchType(MatchType type) { matchType = type; }

      /**
       * Return the number of items that matched the expression.
       *
       * It returns the number of items that did not match if INVERTED match is
       * used.
       *
       * @returns
       *   The match count
       */
00279       int countMatched() const { return matched; }

};

/**
 * Remove the tags that do not singularly match a tag expression.
 *
 * This is a slight abuse of tag expressions, but it can prove useful to remove
 * tags matching, for example, "special::not-yet-tagged*" or
 * "!(use::gaming || game::*)".
 */
template<class ITEM, class TAG>
00291 class FilterTagsByExpression : public Filter<ITEM, TAG>
{
public:
      enum MatchType { PLAIN, INVERTED };

protected:
      Expression expr;
      MatchType matchType;
      int matched;

      bool match(const TAG& tag) const
      {
            OpSet<TAG> tags;
            tags += tag;
            if (matchType == PLAIN)
                  return expr(tags);
            else
                  return !expr(tags);
      }

00311       virtual void consumeItemUntagged(const ITEM& item)
      {
            this->consumer->consume(item);
      }

00316       virtual void consumeItem(const ITEM& item, const OpSet<TAG>& tags)
      {
            OpSet<TAG> outTags;
            for (typename OpSet<TAG>::const_iterator i = tags.begin(); i != tags.end(); i++)
                  if (match(*i))
                  {
                        matched++;
                        outTags += *i;
                  }
            this->consumer->consume(item, outTags);
      }

00328       virtual void consumeItemsUntagged(const OpSet<ITEM>& items)
      {
            this->consumer->consume(items);
      }

00333       virtual void consumeItems(const OpSet<ITEM>& items, const OpSet<TAG>& tags)
      {
            OpSet<TAG> outTags;
            for (typename OpSet<TAG>::const_iterator i = tags.begin();
                        i != tags.end(); i++)
                  if (match(*i))
                  {
                        matched += items.size();
                        outTags += *i;
                  }
            this->consumer->consume(items, outTags);
      }
      
public:     
      FilterTagsByExpression(const Expression& expression) :
            expr(expression), matchType(PLAIN), matched(0) {}
      FilterTagsByExpression(const std::string& expression) :
            expr(expression), matchType(PLAIN), matched(0) {}
      FilterTagsByExpression(Consumer<ITEM, TAG>&cons, const Expression& expression) :
            Filter<ITEM, TAG>(cons),
            expr(expression), matchType(PLAIN), matched(0) {}
      FilterTagsByExpression(Consumer<ITEM, TAG>&cons, const std::string& expression) :
            Filter<ITEM, TAG>(cons),
            expr(expression), matchType(PLAIN), matched(0) {}
      virtual ~FilterTagsByExpression() {}

      /**
       * Set the expression to use for this filter
       *
       * @param expression
       *   The expression to use for matching
       */
00365       void setExpression(const Expression& expression)
      {
            expr = expression;
      }

      /**
       * Set the expression to use for this filter
       *
       * @param expression
       *   The expression to use for matching
       */
00376       void setExpression(const std::string& expression)
      {
            expr = Expression(expression);
      }

      /**
       * Set the type of match
       *
       * @param type
       *   PLAIN: only keep the tags that match the expression
       *   INVERTED: only keep the tags that do not match the expression
       */
00388       void setMatchType(MatchType type) { matchType = type; }

      /**
       * Return the number of tags that matched the expression.
       *
       * It returns the number oftags that did not match if INVERTED match is
       * used.
       *
       * @returns
       *   The match count
       */
00399       int countMatched() const { return matched; }
};

};

// vim:set ts=4 sw=4:
#endif

Generated by  Doxygen 1.6.0   Back to index