]> git.tdb.fi Git - poefilter.git/blobdiff - source/condition.cpp
Merge and conditions if they only differ in one sub-condition
[poefilter.git] / source / condition.cpp
index 42e9ee8f3f1360829d2b518d9d22e99a569f0a69..5978250243f6e51b6fb1ebadeadbbd401f5003bf 100644 (file)
@@ -16,16 +16,30 @@ void CompoundCondition::clone_to(CompoundCondition &other) const
                other.add((*i)->clone());
 }
 
+bool CompoundCondition::sub_equals(const CompoundCondition &other) const
+{
+       if(other.count()!=conditions.size())
+               return false;
+
+       for(unsigned i=0; i<conditions.size(); ++i)
+               if(!conditions[i]->equals(other.get(i)))
+                       return false;
+
+       return true;
+}
+
 void CompoundCondition::add(Condition *cond)
 {
+       if(!cond)
+               throw invalid_argument("CompoundCondition::add");
        conditions.push_back(cond);
 }
 
-const Condition *CompoundCondition::get(unsigned i) const
+const Condition &CompoundCondition::get(unsigned i) const
 {
        if(i>=conditions.size())
                throw out_of_range("CompoundCondition::get");
-       return conditions[i];
+       return *conditions[i];
 }
 
 Condition *CompoundCondition::flatten() const
@@ -40,7 +54,10 @@ Condition *CompoundCondition::flatten() const
                merge = conditions.front()->can_merge(**i, *this);
 
        if(merge)
-               return conditions.front()->merge(conditions, *this);
+       {
+               vector<const Condition *> merge_conds(conditions.begin(), conditions.end());
+               return conditions.front()->merge(merge_conds, *this);
+       }
        else
        {
                Condition *result = 0;
@@ -49,11 +66,15 @@ Condition *CompoundCondition::flatten() const
                        Condition *sub = (*i)->flatten();
                        if(!result)
                                result = sub;
-                       else
+                       else if(sub)
                                result = dispatch_flatten(result, sub);
                }
 
-               return result;
+               if(result && result->is_viable())
+                       return result;
+
+               delete result;
+               return 0;
        }
 }
 
@@ -91,6 +112,69 @@ Condition *CompoundCondition::dispatch_flatten(Condition *cond1, Condition *cond
        throw logic_error("CompoundCondition::dispatch_flatten");
 }
 
+Condition *CompoundCondition::merge_two(Condition *cond1, Condition *cond2, const CompoundCondition &parent, bool del)
+{
+       vector<const Condition *> parts(2);
+       parts[0] = cond1;
+       parts[1] = cond2;
+       Condition *result = cond1->merge(parts, parent);
+
+       if(del)
+       {
+               delete cond1;
+               delete cond2;
+
+               if(!result->is_viable())
+               {
+                       delete result;
+                       return 0;
+               }
+       }
+
+       return result;
+}
+
+Condition *CompoundCondition::add_merged_to(Condition *cond, CompoundCondition *target, bool del)
+{
+       bool merged = false;
+       for(vector<Condition *>::iterator i=target->conditions.begin(); i!=target->conditions.end(); ++i)
+               if((*i)->can_merge(*cond, *target))
+               {
+                       Condition *m = merge_two(cond, *i, *target, false);
+                       delete *i;
+                       if(del)
+                               delete cond;
+                       *i = m;
+                       merged = true;
+                       break;
+               }
+
+       if(!merged)
+               target->add(del ? cond : cond->clone());
+
+       if(del && !target->is_viable())
+       {
+               delete target;
+               return 0;
+       }
+
+       return target;
+}
+
+Condition *CompoundCondition::merge_contents_to(CompoundCondition *cond, CompoundCondition *target)
+{
+       for(vector<Condition *>::iterator i=cond->conditions.begin(); i!=cond->conditions.end(); ++i)
+               add_merged_to(*i, target, false);
+
+       delete cond;
+
+       if(target->is_viable())
+               return target;
+
+       delete target;
+       return 0;
+}
+
 
 AndCondition *AndCondition::clone() const
 {
@@ -99,8 +183,17 @@ AndCondition *AndCondition::clone() const
        return result;
 }
 
+bool AndCondition::equals(const Condition &other) const
+{
+       const AndCondition *other_and = dynamic_cast<const AndCondition *>(&other);
+       return (other_and ? sub_equals(*other_and) : false);
+}
+
 Condition *AndCondition::flatten(Condition *cond1, Condition *cond2) const
 {
+       if(cond1->can_merge(*cond2, *this))
+               return merge_two(cond1, cond2, *this, true);
+
        AndCondition *result = new AndCondition;
        result->add(cond1);
        result->add(cond2);
@@ -109,17 +202,12 @@ Condition *AndCondition::flatten(Condition *cond1, Condition *cond2) const
 
 Condition *AndCondition::flatten(AndCondition *cond1, Condition *cond2) const
 {
-       cond1->add(cond2);
-       return cond1;
+       return add_merged_to(cond2, cond1, true);
 }
 
 Condition *AndCondition::flatten(AndCondition *cond1, AndCondition *cond2) const
 {
-       unsigned count2 = cond2->count();
-       for(unsigned i=0; i<count2; ++i)
-               cond1->add(cond2->get(i)->clone());
-       delete cond2;
-       return cond1;
+       return merge_contents_to(cond2, cond1);
 }
 
 Condition *AndCondition::flatten(OrCondition *cond1, Condition *cond2) const
@@ -127,9 +215,15 @@ Condition *AndCondition::flatten(OrCondition *cond1, Condition *cond2) const
        OrCondition *result = new OrCondition;
        unsigned count = cond1->count();
        for(unsigned i=0; i<count; ++i)
-               result->add(dispatch_flatten(cond1->get(i)->clone(), (i+1<count ? cond2->clone() : cond2)));
+               if(Condition *sub = dispatch_flatten(cond1->get(i).clone(), (i+1<count ? cond2->clone() : cond2)))
+                       add_merged_to(sub, result, true);
        delete cond1;
-       return result;
+
+       if(result->is_viable())
+               return result;
+
+       delete result;
+       return 0;
 }
 
 Condition *AndCondition::flatten(OrCondition *cond1, AndCondition *cond2) const
@@ -144,12 +238,103 @@ Condition *AndCondition::flatten(OrCondition *cond1, OrCondition *cond2) const
        OrCondition *result = new OrCondition;
        for(unsigned i=0; i<count1; ++i)
                for(unsigned j=0; j<count2; ++j)
-                       result->add(dispatch_flatten(cond1->get(i)->clone(), cond2->get(j)->clone()));
+                       if(Condition *sub = dispatch_flatten(cond1->get(i).clone(), cond2->get(j).clone()))
+                               add_merged_to(sub, result, true);
+
        delete cond1;
        delete cond2;
+
+       if(result->is_viable())
+               return result;
+
+       delete result;
+       return 0;
+}
+
+bool AndCondition::can_merge(const Condition &other, const CompoundCondition &parent) const
+{
+       const AndCondition *other_and = dynamic_cast<const AndCondition *>(&other);
+       if(!other_and || !dynamic_cast<const OrCondition *>(&parent))
+               return false;
+
+       return merge(*this, *other_and, parent, 0);
+}
+
+AndCondition *AndCondition::merge(const vector<const Condition *> &conds, const CompoundCondition &parent) const
+{
+       if(conds.size()!=2)
+               return 0;
+
+       const AndCondition *and1 = dynamic_cast<const AndCondition *>(conds[0]);
+       const AndCondition *and2 = dynamic_cast<const AndCondition *>(conds[1]);
+       if(!and1 || !and2 || !dynamic_cast<const OrCondition *>(&parent))
+               return 0;
+
+       AndCondition *result = 0;
+       merge(*and1, *and2, parent, &result);
        return result;
 }
 
+bool AndCondition::merge(const AndCondition &cond1, const AndCondition &cond2, const CompoundCondition &parent, AndCondition **result)
+{
+       if(cond1.count()!=cond2.count())
+               return false;
+
+       int merge1 = -1;
+       vector<bool> used(cond1.count(), false);
+       for(unsigned i=0; i<cond1.count(); ++i)
+       {
+               int match = -1;
+               for(unsigned j=0; (match<0 && j<cond2.count()); ++j)
+                       if(!used[j] && cond1.get(i).equals(cond2.get(j)))
+                               match = j;
+
+               if(match>=0)
+                       used[match] = true;
+               else if(merge1<0)
+                       merge1 = i;
+               else
+                       return false;
+       }
+
+       if(merge1>=0)
+       {
+               vector<const Condition *> merge_conds(2, 0);
+               merge_conds[0] = &cond1.get(merge1);
+               for(unsigned i=0; (!merge_conds[1] && i<cond2.count()); ++i)
+                       if(!used[i])
+                               merge_conds[1] = &cond2.get(i);
+
+               if(!merge_conds[1] || !merge_conds[0]->can_merge(*merge_conds[1], parent))
+                       return false;
+
+               if(result)
+               {
+                       *result = new AndCondition;
+                       (*result)->add(merge_conds[0]->merge(merge_conds, parent));
+               }
+       }
+       else if(result)
+               *result = new AndCondition;
+
+       if(result)
+       {
+               for(unsigned i=0; i<cond2.count(); ++i)
+                       if(used[i])
+                               (*result)->add(cond2.get(i).clone());
+       }
+
+       return true;
+}
+
+bool AndCondition::is_viable() const
+{
+       for(vector<Condition *>::const_iterator i=conditions.begin(); i!=conditions.end(); ++i)
+               if(!(*i)->is_viable())
+                       return false;
+       return !conditions.empty();
+}
+
 void AndCondition::add_lines(list<FilterStatement> &st) const
 {
        for(vector<Condition *>::const_iterator i=conditions.begin(); i!=conditions.end(); ++i)
@@ -164,8 +349,17 @@ OrCondition *OrCondition::clone() const
        return result;
 }
 
+bool OrCondition::equals(const Condition &other) const
+{
+       const OrCondition *other_or = dynamic_cast<const OrCondition *>(&other);
+       return (other_or ? sub_equals(*other_or) : false);
+}
+
 Condition *OrCondition::flatten(Condition *cond1, Condition *cond2) const
 {
+       if(cond1->can_merge(*cond2, *this))
+               return merge_two(cond1, cond2, *this, true);
+
        OrCondition *result = new OrCondition;
        result->add(cond1);
        result->add(cond2);
@@ -184,8 +378,7 @@ Condition *OrCondition::flatten(AndCondition *cond1, AndCondition *cond2) const
 
 Condition *OrCondition::flatten(OrCondition *cond1, Condition *cond2) const
 {
-       cond1->add(cond2);
-       return cond1;
+       return add_merged_to(cond2, cond1, true);
 }
 
 Condition *OrCondition::flatten(OrCondition *cond1, AndCondition *cond2) const
@@ -195,11 +388,15 @@ Condition *OrCondition::flatten(OrCondition *cond1, AndCondition *cond2) const
 
 Condition *OrCondition::flatten(OrCondition *cond1, OrCondition *cond2) const
 {
-       unsigned count2 = cond2->count();
-       for(unsigned i=0; i<count2; ++i)
-               cond1->add(cond2->get(i)->clone());
-       delete cond2;
-       return cond1;
+       return merge_contents_to(cond2, cond1);
+}
+
+bool OrCondition::is_viable() const
+{
+       for(vector<Condition *>::const_iterator i=conditions.begin(); i!=conditions.end(); ++i)
+               if((*i)->is_viable())
+                       return true;
+       return false;
 }
 
 void OrCondition::add_lines(list<FilterStatement> &st) const
@@ -224,6 +421,23 @@ LinkedColorsCondition *LinkedColorsCondition::clone() const
        return new LinkedColorsCondition(colors);
 }
 
+bool LinkedColorsCondition::equals(const Condition &other) const
+{
+       const LinkedColorsCondition *other_linked = dynamic_cast<const LinkedColorsCondition *>(&other);
+       if(!other_linked)
+               return false;
+
+       for(unsigned i=0; i<7; ++i)
+       {
+               if(colors.colors[i]!=other_linked->colors.colors[i])
+                       return false;
+               if(!colors.colors[i])
+                       break;
+       }
+
+       return true;
+}
+
 void LinkedColorsCondition::add_lines(list<FilterStatement> &st) const
 {
        FilterStatement::add_line(st, format("SocketGroup %s", colors.colors));