]> git.tdb.fi Git - poefilter.git/blob - source/condition.cpp
Merge and conditions if they only differ in one sub-condition
[poefilter.git] / source / condition.cpp
1 #include "condition.h"
2 #include "filter.h"
3
4 using namespace std;
5 using namespace Msp;
6
7 CompoundCondition::~CompoundCondition()
8 {
9         for(vector<Condition *>::const_iterator i=conditions.begin(); i!=conditions.end(); ++i)
10                 delete *i;
11 }
12
13 void CompoundCondition::clone_to(CompoundCondition &other) const
14 {
15         for(vector<Condition *>::const_iterator i=conditions.begin(); i!=conditions.end(); ++i)
16                 other.add((*i)->clone());
17 }
18
19 bool CompoundCondition::sub_equals(const CompoundCondition &other) const
20 {
21         if(other.count()!=conditions.size())
22                 return false;
23
24         for(unsigned i=0; i<conditions.size(); ++i)
25                 if(!conditions[i]->equals(other.get(i)))
26                         return false;
27
28         return true;
29 }
30
31 void CompoundCondition::add(Condition *cond)
32 {
33         if(!cond)
34                 throw invalid_argument("CompoundCondition::add");
35         conditions.push_back(cond);
36 }
37
38 const Condition &CompoundCondition::get(unsigned i) const
39 {
40         if(i>=conditions.size())
41                 throw out_of_range("CompoundCondition::get");
42         return *conditions[i];
43 }
44
45 Condition *CompoundCondition::flatten() const
46 {
47         if(conditions.empty())
48                 return 0;
49         else if(conditions.size()==1)
50                 return conditions.front()->clone();
51
52         bool merge = true;
53         for(vector<Condition *>::const_iterator i=conditions.begin(); (merge && ++i!=conditions.end()); )
54                 merge = conditions.front()->can_merge(**i, *this);
55
56         if(merge)
57         {
58                 vector<const Condition *> merge_conds(conditions.begin(), conditions.end());
59                 return conditions.front()->merge(merge_conds, *this);
60         }
61         else
62         {
63                 Condition *result = 0;
64                 for(vector<Condition *>::const_iterator i=conditions.begin(); i!=conditions.end(); ++i)
65                 {
66                         Condition *sub = (*i)->flatten();
67                         if(!result)
68                                 result = sub;
69                         else if(sub)
70                                 result = dispatch_flatten(result, sub);
71                 }
72
73                 if(result && result->is_viable())
74                         return result;
75
76                 delete result;
77                 return 0;
78         }
79 }
80
81 Condition *CompoundCondition::dispatch_flatten(Condition *cond1, Condition *cond2) const
82 {
83         OrCondition *or1 = dynamic_cast<OrCondition *>(cond1);
84         AndCondition *and1 = dynamic_cast<AndCondition *>(cond1);
85         OrCondition *or2 = dynamic_cast<OrCondition *>(cond2);
86         AndCondition *and2 = dynamic_cast<AndCondition *>(cond2);
87         if(or1 || or2)
88         {
89                 if(or1 && or2)
90                         return flatten(or1, or2);
91                 else if(or1 && and2)
92                         return flatten(or1, and2);
93                 else if(or2 && and1)
94                         return flatten(or2, and1);
95                 else if(or1)
96                         return flatten(or1, cond2);
97                 else if(or2)
98                         return flatten(or2, cond1);
99         }
100         else if(and1 || and2)
101         {
102                 if(and1 && and2)
103                         return flatten(and1, and2);
104                 else if(and1)
105                         return flatten(and1, cond2);
106                 else if(and2)
107                         return flatten(and2, cond1);
108         }
109         else
110                 return flatten(cond1, cond2);
111
112         throw logic_error("CompoundCondition::dispatch_flatten");
113 }
114
115 Condition *CompoundCondition::merge_two(Condition *cond1, Condition *cond2, const CompoundCondition &parent, bool del)
116 {
117         vector<const Condition *> parts(2);
118         parts[0] = cond1;
119         parts[1] = cond2;
120         Condition *result = cond1->merge(parts, parent);
121
122         if(del)
123         {
124                 delete cond1;
125                 delete cond2;
126
127                 if(!result->is_viable())
128                 {
129                         delete result;
130                         return 0;
131                 }
132         }
133
134         return result;
135 }
136
137 Condition *CompoundCondition::add_merged_to(Condition *cond, CompoundCondition *target, bool del)
138 {
139         bool merged = false;
140         for(vector<Condition *>::iterator i=target->conditions.begin(); i!=target->conditions.end(); ++i)
141                 if((*i)->can_merge(*cond, *target))
142                 {
143                         Condition *m = merge_two(cond, *i, *target, false);
144                         delete *i;
145                         if(del)
146                                 delete cond;
147                         *i = m;
148                         merged = true;
149                         break;
150                 }
151
152         if(!merged)
153                 target->add(del ? cond : cond->clone());
154
155         if(del && !target->is_viable())
156         {
157                 delete target;
158                 return 0;
159         }
160
161         return target;
162 }
163
164 Condition *CompoundCondition::merge_contents_to(CompoundCondition *cond, CompoundCondition *target)
165 {
166         for(vector<Condition *>::iterator i=cond->conditions.begin(); i!=cond->conditions.end(); ++i)
167                 add_merged_to(*i, target, false);
168
169         delete cond;
170
171         if(target->is_viable())
172                 return target;
173
174         delete target;
175         return 0;
176 }
177
178
179 AndCondition *AndCondition::clone() const
180 {
181         AndCondition *result = new AndCondition;
182         clone_to(*result);
183         return result;
184 }
185
186 bool AndCondition::equals(const Condition &other) const
187 {
188         const AndCondition *other_and = dynamic_cast<const AndCondition *>(&other);
189         return (other_and ? sub_equals(*other_and) : false);
190 }
191
192 Condition *AndCondition::flatten(Condition *cond1, Condition *cond2) const
193 {
194         if(cond1->can_merge(*cond2, *this))
195                 return merge_two(cond1, cond2, *this, true);
196
197         AndCondition *result = new AndCondition;
198         result->add(cond1);
199         result->add(cond2);
200         return result;
201 }
202
203 Condition *AndCondition::flatten(AndCondition *cond1, Condition *cond2) const
204 {
205         return add_merged_to(cond2, cond1, true);
206 }
207
208 Condition *AndCondition::flatten(AndCondition *cond1, AndCondition *cond2) const
209 {
210         return merge_contents_to(cond2, cond1);
211 }
212
213 Condition *AndCondition::flatten(OrCondition *cond1, Condition *cond2) const
214 {
215         OrCondition *result = new OrCondition;
216         unsigned count = cond1->count();
217         for(unsigned i=0; i<count; ++i)
218                 if(Condition *sub = dispatch_flatten(cond1->get(i).clone(), (i+1<count ? cond2->clone() : cond2)))
219                         add_merged_to(sub, result, true);
220         delete cond1;
221
222         if(result->is_viable())
223                 return result;
224
225         delete result;
226         return 0;
227 }
228
229 Condition *AndCondition::flatten(OrCondition *cond1, AndCondition *cond2) const
230 {
231         return flatten(cond1, static_cast<Condition *>(cond2));
232 }
233
234 Condition *AndCondition::flatten(OrCondition *cond1, OrCondition *cond2) const
235 {
236         unsigned count1 = cond1->count();
237         unsigned count2 = cond2->count();
238         OrCondition *result = new OrCondition;
239         for(unsigned i=0; i<count1; ++i)
240                 for(unsigned j=0; j<count2; ++j)
241                         if(Condition *sub = dispatch_flatten(cond1->get(i).clone(), cond2->get(j).clone()))
242                                 add_merged_to(sub, result, true);
243
244         delete cond1;
245         delete cond2;
246
247         if(result->is_viable())
248                 return result;
249
250         delete result;
251         return 0;
252 }
253
254 bool AndCondition::can_merge(const Condition &other, const CompoundCondition &parent) const
255 {
256         const AndCondition *other_and = dynamic_cast<const AndCondition *>(&other);
257         if(!other_and || !dynamic_cast<const OrCondition *>(&parent))
258                 return false;
259
260         return merge(*this, *other_and, parent, 0);
261 }
262
263 AndCondition *AndCondition::merge(const vector<const Condition *> &conds, const CompoundCondition &parent) const
264 {
265         if(conds.size()!=2)
266                 return 0;
267
268         const AndCondition *and1 = dynamic_cast<const AndCondition *>(conds[0]);
269         const AndCondition *and2 = dynamic_cast<const AndCondition *>(conds[1]);
270         if(!and1 || !and2 || !dynamic_cast<const OrCondition *>(&parent))
271                 return 0;
272
273         AndCondition *result = 0;
274         merge(*and1, *and2, parent, &result);
275         return result;
276 }
277
278 bool AndCondition::merge(const AndCondition &cond1, const AndCondition &cond2, const CompoundCondition &parent, AndCondition **result)
279 {
280         if(cond1.count()!=cond2.count())
281                 return false;
282
283         int merge1 = -1;
284         vector<bool> used(cond1.count(), false);
285         for(unsigned i=0; i<cond1.count(); ++i)
286         {
287                 int match = -1;
288                 for(unsigned j=0; (match<0 && j<cond2.count()); ++j)
289                         if(!used[j] && cond1.get(i).equals(cond2.get(j)))
290                                 match = j;
291
292                 if(match>=0)
293                         used[match] = true;
294                 else if(merge1<0)
295                         merge1 = i;
296                 else
297                         return false;
298         }
299
300         if(merge1>=0)
301         {
302                 vector<const Condition *> merge_conds(2, 0);
303                 merge_conds[0] = &cond1.get(merge1);
304                 for(unsigned i=0; (!merge_conds[1] && i<cond2.count()); ++i)
305                         if(!used[i])
306                                 merge_conds[1] = &cond2.get(i);
307
308                 if(!merge_conds[1] || !merge_conds[0]->can_merge(*merge_conds[1], parent))
309                         return false;
310
311                 if(result)
312                 {
313                         *result = new AndCondition;
314                         (*result)->add(merge_conds[0]->merge(merge_conds, parent));
315                 }
316         }
317         else if(result)
318                 *result = new AndCondition;
319
320         if(result)
321         {
322                 for(unsigned i=0; i<cond2.count(); ++i)
323                         if(used[i])
324                                 (*result)->add(cond2.get(i).clone());
325         }
326
327         return true;
328 }
329
330 bool AndCondition::is_viable() const
331 {
332         for(vector<Condition *>::const_iterator i=conditions.begin(); i!=conditions.end(); ++i)
333                 if(!(*i)->is_viable())
334                         return false;
335         return !conditions.empty();
336 }
337
338 void AndCondition::add_lines(list<FilterStatement> &st) const
339 {
340         for(vector<Condition *>::const_iterator i=conditions.begin(); i!=conditions.end(); ++i)
341                 (*i)->add_lines(st);
342 }
343
344
345 OrCondition *OrCondition::clone() const
346 {
347         OrCondition *result = new OrCondition;
348         clone_to(*result);
349         return result;
350 }
351
352 bool OrCondition::equals(const Condition &other) const
353 {
354         const OrCondition *other_or = dynamic_cast<const OrCondition *>(&other);
355         return (other_or ? sub_equals(*other_or) : false);
356 }
357
358 Condition *OrCondition::flatten(Condition *cond1, Condition *cond2) const
359 {
360         if(cond1->can_merge(*cond2, *this))
361                 return merge_two(cond1, cond2, *this, true);
362
363         OrCondition *result = new OrCondition;
364         result->add(cond1);
365         result->add(cond2);
366         return result;
367 }
368
369 Condition *OrCondition::flatten(AndCondition *cond1, Condition *cond2) const
370 {
371         return flatten(static_cast<Condition *>(cond1), cond2);
372 }
373
374 Condition *OrCondition::flatten(AndCondition *cond1, AndCondition *cond2) const
375 {
376         return flatten(static_cast<Condition *>(cond1), static_cast<Condition *>(cond2));
377 }
378
379 Condition *OrCondition::flatten(OrCondition *cond1, Condition *cond2) const
380 {
381         return add_merged_to(cond2, cond1, true);
382 }
383
384 Condition *OrCondition::flatten(OrCondition *cond1, AndCondition *cond2) const
385 {
386         return flatten(cond1, static_cast<Condition *>(cond2));
387 }
388
389 Condition *OrCondition::flatten(OrCondition *cond1, OrCondition *cond2) const
390 {
391         return merge_contents_to(cond2, cond1);
392 }
393
394 bool OrCondition::is_viable() const
395 {
396         for(vector<Condition *>::const_iterator i=conditions.begin(); i!=conditions.end(); ++i)
397                 if((*i)->is_viable())
398                         return true;
399         return false;
400 }
401
402 void OrCondition::add_lines(list<FilterStatement> &st) const
403 {
404         list<FilterStatement> result;
405         for(vector<Condition *>::const_iterator i=conditions.begin(); i!=conditions.end(); ++i)
406         {
407                 list<FilterStatement> sub_result = st;
408                 (*i)->add_lines(sub_result);
409                 result.splice(result.end(), sub_result);
410         }
411         swap(result, st);
412 }
413
414
415 LinkedColorsCondition::LinkedColorsCondition(const Colors &c):
416         colors(c)
417 { }
418
419 LinkedColorsCondition *LinkedColorsCondition::clone() const
420 {
421         return new LinkedColorsCondition(colors);
422 }
423
424 bool LinkedColorsCondition::equals(const Condition &other) const
425 {
426         const LinkedColorsCondition *other_linked = dynamic_cast<const LinkedColorsCondition *>(&other);
427         if(!other_linked)
428                 return false;
429
430         for(unsigned i=0; i<7; ++i)
431         {
432                 if(colors.colors[i]!=other_linked->colors.colors[i])
433                         return false;
434                 if(!colors.colors[i])
435                         break;
436         }
437
438         return true;
439 }
440
441 void LinkedColorsCondition::add_lines(list<FilterStatement> &st) const
442 {
443         FilterStatement::add_line(st, format("SocketGroup %s", colors.colors));
444 }
445
446
447 void operator>>(const LexicalConverter &conv, LinkedColorsCondition::Colors &colors)
448 {
449         const string &str = conv.get();
450         bool rgb = true;
451         for(string::const_iterator i=str.begin(); (rgb && i!=str.end()); ++i)
452                 rgb = (*i=='R' || *i=='G' || *i=='B');
453         if(str.size()>6 || !rgb)
454                 throw lexical_error(format("conversion of '%s' to LinkedColorsCondition::Colors", str));
455
456         fill(colors.colors, colors.colors+7, '\0');
457         copy(str.begin(), str.end(), colors.colors);
458 }