File indexing completed on 2024-05-12 16:35:15

0001 /* This file is part of the KDE project
0002    Copyright 2007 Stefan Nikolaus <stefan.nikolaus@kdemail.net>
0003 
0004    This library is free software; you can redistribute it and/or
0005    modify it under the terms of the GNU Library General Public
0006    License as published by the Free Software Foundation; either
0007    version 2 of the License, or (at your option) any later version.
0008 
0009    This library is distributed in the hope that it will be useful,
0010    but WITHOUT ANY WARRANTY; without even the implied warranty of
0011    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0012    Library General Public License for more details.
0013 
0014    You should have received a copy of the GNU Library General Public License
0015    along with this library; see the file COPYING.LIB.  If not, write to
0016    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0017    Boston, MA 02110-1301, USA.
0018 */
0019 
0020 #include "Filter.h"
0021 
0022 #include <QList>
0023 #include <QRect>
0024 
0025 #include <KoXmlNS.h>
0026 #include <KoXmlWriter.h>
0027 
0028 #include "CellStorage.h"
0029 #include "Database.h"
0030 #include "Map.h"
0031 #include "Region.h"
0032 #include "Sheet.h"
0033 #include "Value.h"
0034 #include "ValueConverter.h"
0035 #include "odf/SheetsOdf.h"
0036 
0037 using namespace Calligra::Sheets;
0038 
0039 class Calligra::Sheets::AbstractCondition
0040 {
0041 public:
0042     virtual ~AbstractCondition() {}
0043     enum Type { And, Or, Condition };
0044     virtual Type type() const = 0;
0045     virtual bool loadOdf(const KoXmlElement& element) = 0;
0046     virtual void saveOdf(KoXmlWriter& xmlWriter) = 0;
0047     virtual bool evaluate(const Database& database, int index) const = 0;
0048     virtual bool isEmpty() const = 0;
0049     virtual QHash<QString, Filter::Comparison> conditions(int fieldNumber) const = 0;
0050     virtual void removeConditions(int fieldNumber) = 0;
0051     virtual QString dump() const = 0;
0052 
0053     static bool listsAreEqual(const QList<AbstractCondition*>& a, const QList<AbstractCondition*>& b);
0054 };
0055 
0056 /**
0057  * OpenDocument, 8.7.2 Filter And
0058  */
0059 class Filter::And : public AbstractCondition
0060 {
0061 public:
0062     And() {}
0063     And(const And& other);
0064     And& operator=(const And& other);
0065     ~And() override {
0066         qDeleteAll(list);
0067     }
0068     Type type() const override {
0069         return AbstractCondition::And;
0070     }
0071     bool loadOdf(const KoXmlElement& parent) override;
0072     void saveOdf(KoXmlWriter& xmlWriter) override {
0073         if (!list.count())
0074             return;
0075         xmlWriter.startElement("table:filter-and");
0076         for (int i = 0; i < list.count(); ++i)
0077             list[i]->saveOdf(xmlWriter);
0078         xmlWriter.endElement();
0079     }
0080     bool evaluate(const Database& database, int index) const override {
0081         for (int i = 0; i < list.count(); ++i) {
0082             // lazy evaluation, stop on first false
0083             if (!list[i]->evaluate(database, index))
0084                 return false;
0085         }
0086         return true;
0087     }
0088     bool isEmpty() const override {
0089         return list.isEmpty();
0090     }
0091     QHash<QString, Filter::Comparison> conditions(int fieldNumber) const override {
0092         QHash<QString, Filter::Comparison> result;
0093         for (int i = 0; i < list.count(); ++i)
0094             result.unite(list[i]->conditions(fieldNumber));
0095         return result;
0096     }
0097     void removeConditions(int fieldNumber) override {
0098         QList<AbstractCondition*> newList;
0099         for (int i = 0; i < list.count(); ++i) {
0100             list[i]->removeConditions(fieldNumber);
0101             if (!list[i]->isEmpty())
0102                 newList.append(list[i]);
0103             else
0104                 delete list[i];
0105         }
0106         list = newList;
0107     }
0108     bool operator!=(const And& other) const {
0109         return !listsAreEqual(list, other.list);
0110     }
0111     QString dump() const override {
0112         QString result = "\t";
0113         for (int i = 0; i < list.count(); ++i) {
0114             if (i)
0115                 result += "AND\t";
0116             result += list[i]->dump();
0117         }
0118         return result;
0119     }
0120 
0121 public:
0122     QList<AbstractCondition*> list; // allowed: Or or Condition
0123 };
0124 
0125 /**
0126  * OpenDocument, 8.7.3 Filter Or
0127  */
0128 class Filter::Or : public AbstractCondition
0129 {
0130 public:
0131     Or() {}
0132     Or(const Or& other);
0133     Or& operator=(const Or& other);
0134     ~Or() override {
0135         qDeleteAll(list);
0136     }
0137     Type type() const override {
0138         return AbstractCondition::Or;
0139     }
0140     bool loadOdf(const KoXmlElement& element) override;
0141     void saveOdf(KoXmlWriter& xmlWriter) override {
0142         if (!list.count())
0143             return;
0144         xmlWriter.startElement("table:filter-or");
0145         for (int i = 0; i < list.count(); ++i)
0146             list[i]->saveOdf(xmlWriter);
0147         xmlWriter.endElement();
0148     }
0149     bool evaluate(const Database& database, int index) const override {
0150         for (int i = 0; i < list.count(); ++i) {
0151             // lazy evaluation, stop on first true
0152             if (list[i]->evaluate(database, index))
0153                 return true;
0154         }
0155         return false;
0156     }
0157     bool isEmpty() const override {
0158         return list.isEmpty();
0159     }
0160     QHash<QString, Filter::Comparison> conditions(int fieldNumber) const override {
0161         QHash<QString, Filter::Comparison> result;
0162         for (int i = 0; i < list.count(); ++i)
0163             result.unite(list[i]->conditions(fieldNumber));
0164         return result;
0165     }
0166     void removeConditions(int fieldNumber) override {
0167         QList<AbstractCondition*> newList;
0168         for (int i = 0; i < list.count(); ++i) {
0169             list[i]->removeConditions(fieldNumber);
0170             if (!list[i]->isEmpty())
0171                 newList.append(list[i]);
0172             else
0173                 delete list[i];
0174         }
0175         list = newList;
0176     }
0177     bool operator!=(const Or& other) const {
0178         return !listsAreEqual(list, other.list);
0179     }
0180     QString dump() const override {
0181         QString result = "\t";
0182         for (int i = 0; i < list.count(); ++i) {
0183             if (i)
0184                 result += "OR\t";
0185             result += list[i]->dump();
0186         }
0187         return result;
0188     }
0189 
0190 public:
0191     QList<AbstractCondition*> list; // allowed: And or Condition
0192 };
0193 
0194 /**
0195  * OpenDocument, 8.7.4 Filter Condition
0196  */
0197 class Filter::Condition : public AbstractCondition
0198 {
0199 public:
0200     Condition()
0201             : fieldNumber(-1)
0202             , operation(Match)
0203             , caseSensitivity(Qt::CaseInsensitive)
0204             , dataType(Text) {
0205     }
0206     Condition(int _fieldNumber, Comparison _comparison, const QString& _value,
0207               Qt::CaseSensitivity _caseSensitivity, Mode _mode)
0208             : fieldNumber(_fieldNumber)
0209             , value(_value)
0210             , operation(_comparison)
0211             , caseSensitivity(_caseSensitivity)
0212             , dataType(_mode) {
0213     }
0214     Condition(const Condition& other)
0215             : AbstractCondition()
0216             , fieldNumber(other.fieldNumber)
0217             , value(other.value)
0218             , operation(other.operation)
0219             , caseSensitivity(other.caseSensitivity)
0220             , dataType(other.dataType) {
0221     }
0222     Condition& operator=(const Condition& other);
0223     ~Condition() override {}
0224 
0225     Type type() const override {
0226         return AbstractCondition::Condition;
0227     }
0228     bool loadOdf(const KoXmlElement& element) override {
0229         if (element.hasAttributeNS(KoXmlNS::table, "field-number")) {
0230             bool ok = false;
0231             fieldNumber = element.attributeNS(KoXmlNS::table, "field-number", QString()).toInt(&ok);
0232             if (!ok || fieldNumber < 0)
0233                 return false;
0234         }
0235         if (element.hasAttributeNS(KoXmlNS::table, "value"))
0236             value = element.attributeNS(KoXmlNS::table, "value", QString());
0237         if (element.hasAttributeNS(KoXmlNS::table, "operator")) {
0238             const QString string = element.attributeNS(KoXmlNS::table, "operator", QString());
0239             if (string == "match")
0240                 operation = Match;
0241             else if (string == "!match")
0242                 operation = NotMatch;
0243             else if (string == "=")
0244                 operation = Equal;
0245             else if (string == "!=")
0246                 operation = NotEqual;
0247             else if (string == "<")
0248                 operation = Less;
0249             else if (string == ">")
0250                 operation = Greater;
0251             else if (string == "<=")
0252                 operation = LessOrEqual;
0253             else if (string == ">=")
0254                 operation = GreaterOrEqual;
0255             else if (string == "empty")
0256                 operation = Empty;
0257             else if (string == "!empty")
0258                 operation = NotEmpty;
0259             else if (string == "top values")
0260                 operation = TopValues;
0261             else if (string == "bottom values")
0262                 operation = BottomValues;
0263             else if (string == "top percent")
0264                 operation = TopPercent;
0265             else if (string == "bottom percent")
0266                 operation = BottomPercent;
0267             else {
0268                 debugSheets << "table:operator: unknown value";
0269                 return false;
0270             }
0271         }
0272         if (element.hasAttributeNS(KoXmlNS::table, "case-sensitive")) {
0273             if (element.attributeNS(KoXmlNS::table, "case-sensitive", "false") == "true")
0274                 caseSensitivity = Qt::CaseSensitive;
0275             else
0276                 caseSensitivity = Qt::CaseInsensitive;
0277         }
0278         if (element.hasAttributeNS(KoXmlNS::table, "data-type")) {
0279             if (element.attributeNS(KoXmlNS::table, "data-type", "text") == "number")
0280                 dataType = Number;
0281             else
0282                 dataType = Text;
0283         }
0284         return true;
0285     }
0286     void saveOdf(KoXmlWriter& xmlWriter) override {
0287         if (fieldNumber < 0)
0288             return;
0289         xmlWriter.startElement("table:filter-condition");
0290         xmlWriter.addAttribute("table:field-number", fieldNumber);
0291         xmlWriter.addAttribute("table:value", value);
0292         switch (operation) {
0293         case Match:
0294             xmlWriter.addAttribute("table:operator", "match");
0295             break;
0296         case NotMatch:
0297             xmlWriter.addAttribute("table:operator", "!match");
0298             break;
0299         case Equal:
0300             xmlWriter.addAttribute("table:operator", "=");
0301             break;
0302         case NotEqual:
0303             xmlWriter.addAttribute("table:operator", "!=");
0304             break;
0305         case Less:
0306             xmlWriter.addAttribute("table:operator", "<");
0307             break;
0308         case Greater:
0309             xmlWriter.addAttribute("table:operator", ">");
0310             break;
0311         case LessOrEqual:
0312             xmlWriter.addAttribute("table:operator", "<=");
0313             break;
0314         case GreaterOrEqual:
0315             xmlWriter.addAttribute("table:operator", ">=");
0316             break;
0317         case Empty:
0318             xmlWriter.addAttribute("table:operator", "empty");
0319             break;
0320         case NotEmpty:
0321             xmlWriter.addAttribute("table:operator", "!empty");
0322             break;
0323         case TopValues:
0324             xmlWriter.addAttribute("table:operator", "top values");
0325             break;
0326         case BottomValues:
0327             xmlWriter.addAttribute("table:operator", "bottom values");
0328             break;
0329         case TopPercent:
0330             xmlWriter.addAttribute("table:operator", "top percent");
0331             break;
0332         case BottomPercent:
0333             xmlWriter.addAttribute("table:operator", "bottom percent");
0334             break;
0335         }
0336         if (caseSensitivity == Qt::CaseSensitive)
0337             xmlWriter.addAttribute("table:case-sensitive", true);
0338         if (dataType == Number)
0339             xmlWriter.addAttribute("table:data-type", "number");
0340         xmlWriter.endElement();
0341     }
0342     bool evaluate(const Database& database, int index) const override {
0343         const Sheet* sheet = database.range().lastSheet();
0344         const QRect range = database.range().lastRange();
0345         const int start = database.orientation() == Qt::Vertical ? range.left() : range.top();
0346 //         debugSheets <<"index:" << index <<" start:" << start <<" fieldNumber:" << fieldNumber;
0347         const Value value = database.orientation() == Qt::Vertical
0348                             ? sheet->cellStorage()->value(start + fieldNumber, index)
0349                             : sheet->cellStorage()->value(index, start + fieldNumber);
0350         const QString testString = sheet->map()->converter()->asString(value).asString();
0351         switch (operation) {
0352         case Match: {
0353             const bool result = QString::compare(this->value, testString, caseSensitivity) == 0;
0354 //                 debugSheets <<"Match" << this->value <<"?" << testString <<"" << result;
0355             if (result)
0356                 return true;
0357             break;
0358         }
0359         case NotMatch: {
0360             const bool result = QString::compare(this->value, testString, caseSensitivity) != 0;
0361 //                 debugSheets <<"Not Match" << this->value <<"?" << testString <<"" << result;
0362             if (result)
0363                 return true;
0364             break;
0365         }
0366         default:
0367             break;
0368         }
0369         return false;
0370     }
0371     bool isEmpty() const override {
0372         return fieldNumber == -1;
0373     }
0374     QHash<QString, Filter::Comparison> conditions(int fieldNumber) const override {
0375         QHash<QString, Filter::Comparison> result;
0376         if (this->fieldNumber == fieldNumber)
0377             result.insert(value, operation);
0378         return result;
0379     }
0380     void removeConditions(int fieldNumber) override {
0381         if (this->fieldNumber == fieldNumber) {
0382 //             debugSheets <<"removing condition for fieldNumber" << fieldNumber;
0383             this->fieldNumber = -1;
0384         }
0385     }
0386     bool operator==(const Condition& other) const {
0387         if (fieldNumber != other.fieldNumber)
0388             return false;
0389         if (value != other.value)
0390             return false;
0391         if (operation != other.operation)
0392             return false;
0393         if (caseSensitivity != other.caseSensitivity)
0394             return false;
0395         if (dataType != other.dataType)
0396             return false;
0397         return true;
0398     }
0399     bool operator!=(const Condition& other) const {
0400         return !operator==(other);
0401     }
0402     QString dump() const override {
0403         QString result = QString("fieldNumber: %1 ").arg(fieldNumber);
0404         switch (operation) {
0405         case Match:     result += "Match"; break;
0406         case NotMatch:  result += "Not Match"; break;
0407         default:        break;
0408         }
0409         return result + " value: " + value + '\n';
0410     }
0411 
0412 public:
0413     int fieldNumber;
0414     QString value; // Value?
0415     Comparison operation;
0416     Qt::CaseSensitivity caseSensitivity;
0417     Mode dataType;
0418 };
0419 
0420 Filter::And::And(const And& other)
0421         : AbstractCondition()
0422 {
0423     for (int i = 0; i < other.list.count(); ++i) {
0424         if (!other.list[i])
0425             continue;
0426         else if (other.list[i]->type() == AbstractCondition::And)
0427             list.append(new Filter::And(*static_cast<Filter::And*>(other.list[i])));
0428         else if (other.list[i]->type() == AbstractCondition::Or)
0429             list.append(new Filter::Or(*static_cast<Filter::Or*>(other.list[i])));
0430         else
0431             list.append(new Filter::Condition(*static_cast<Filter::Condition*>(other.list[i])));
0432     }
0433 }
0434 
0435 bool Filter::And::loadOdf(const KoXmlElement& parent)
0436 {
0437     KoXmlElement element;
0438     AbstractCondition* condition;
0439     forEachElement(element, parent) {
0440         if (element.namespaceURI() != KoXmlNS::table)
0441             continue;
0442         if (element.localName() == "filter-or")
0443             condition = new Filter::Or();
0444         else if (element.localName() == "filter-condition")
0445             condition = new Filter::Condition();
0446         else
0447             continue;
0448         if (condition->loadOdf(element))
0449             list.append(condition);
0450         else
0451             delete condition;
0452     }
0453     return !list.isEmpty();
0454 }
0455 
0456 Filter::Or::Or(const Or& other)
0457         : AbstractCondition()
0458 {
0459     for (int i = 0; i < other.list.count(); ++i) {
0460         if (!other.list[i])
0461             continue;
0462         else if (other.list[i]->type() == AbstractCondition::And)
0463             list.append(new Filter::And(*static_cast<Filter::And*>(other.list[i])));
0464         else if (other.list[i]->type() == AbstractCondition::Or)
0465             list.append(new Filter::Or(*static_cast<Filter::Or*>(other.list[i])));
0466         else
0467             list.append(new Filter::Condition(*static_cast<Filter::Condition*>(other.list[i])));
0468     }
0469 }
0470 
0471 bool Filter::Or::loadOdf(const KoXmlElement& parent)
0472 {
0473     KoXmlElement element;
0474     AbstractCondition* condition;
0475     forEachElement(element, parent) {
0476         if (element.namespaceURI() != KoXmlNS::table)
0477             continue;
0478         if (element.localName() == "filter-and")
0479             condition = new Filter::And();
0480         else if (element.localName() == "filter-condition")
0481             condition = new Filter::Condition();
0482         else
0483             continue;
0484         if (condition->loadOdf(element))
0485             list.append(condition);
0486         else
0487             delete condition;
0488     }
0489     return !list.isEmpty();
0490 }
0491 
0492 
0493 class Q_DECL_HIDDEN Filter::Private
0494 {
0495 public:
0496     Private()
0497             : condition(0)
0498             , conditionSource(Self)
0499             , displayDuplicates(true) {
0500     }
0501 
0502     AbstractCondition* condition;
0503     Region targetRangeAddress;
0504     enum { Self, CellRange } conditionSource;
0505     Region conditionSourceRangeAddress;
0506     bool displayDuplicates;
0507 };
0508 
0509 Filter::Filter()
0510         : d(new Private)
0511 {
0512 }
0513 
0514 Filter::Filter(const Filter& other)
0515         : d(new Private)
0516 {
0517     if (!other.d->condition)
0518         d->condition = 0;
0519     else if (other.d->condition->type() == AbstractCondition::And)
0520         d->condition = new And(*static_cast<And*>(other.d->condition));
0521     else if (other.d->condition->type() == AbstractCondition::Or)
0522         d->condition = new Or(*static_cast<Or*>(other.d->condition));
0523     else
0524         d->condition = new Condition(*static_cast<Condition*>(other.d->condition));
0525     d->targetRangeAddress = other.d->targetRangeAddress;
0526     d->conditionSource = other.d->conditionSource;
0527     d->conditionSourceRangeAddress = other.d->conditionSourceRangeAddress;
0528     d->displayDuplicates = other.d->displayDuplicates;
0529 }
0530 
0531 Filter::~Filter()
0532 {
0533     delete d->condition;
0534     delete d;
0535 }
0536 
0537 void Filter::addCondition(Composition composition,
0538                           int fieldNumber, Comparison comparison, const QString& value,
0539                           Qt::CaseSensitivity caseSensitivity, Mode mode)
0540 {
0541     Condition* condition = new Condition(fieldNumber, comparison, value, caseSensitivity, mode);
0542     if (!d->condition) {
0543         d->condition = condition;
0544     } else if (composition == AndComposition) {
0545         if (d->condition->type() == AbstractCondition::And) {
0546             static_cast<And*>(d->condition)->list.append(condition);
0547         } else {
0548             And* andComposition = new And();
0549             andComposition->list.append(d->condition);
0550             andComposition->list.append(condition);
0551             d->condition = andComposition;
0552         }
0553     } else { // composition == OrComposition
0554         if (d->condition->type() == AbstractCondition::Or) {
0555             static_cast<Or*>(d->condition)->list.append(condition);
0556         } else {
0557             Or* orComposition = new Or();
0558             orComposition->list.append(d->condition);
0559             orComposition->list.append(condition);
0560             d->condition = orComposition;
0561         }
0562     }
0563 }
0564 
0565 QList<AbstractCondition*> Filter::copyList(const QList<AbstractCondition*>& list)
0566 {
0567     QList<AbstractCondition*> out;
0568     foreach (AbstractCondition* c, list) {
0569         if (!c) {
0570             continue;
0571         } else if (c->type() == AbstractCondition::And) {
0572             out.append(new Filter::And(*static_cast<Filter::And*>(c)));
0573         } else if (c->type() == AbstractCondition::Or) {
0574             out.append(new Filter::Or(*static_cast<Filter::Or*>(c)));
0575         } else {
0576             out.append(new Filter::Condition(*static_cast<Filter::Condition*>(c)));
0577         }
0578     }
0579     return out;
0580 }
0581 
0582 void Filter::addSubFilter(Composition composition, const Filter& filter)
0583 {
0584     if (!d->condition) {
0585         if (!filter.d->condition)
0586             d->condition = 0;
0587         else if (filter.d->condition->type() == AbstractCondition::And)
0588             d->condition = new And(*static_cast<And*>(filter.d->condition));
0589         else if (filter.d->condition->type() == AbstractCondition::Or)
0590             d->condition = new Or(*static_cast<Or*>(filter.d->condition));
0591         else // if (filter.d->condition->type() == AbstractCondition::Condition)
0592             d->condition = new Condition(*static_cast<Condition*>(filter.d->condition));
0593     } else if (composition == AndComposition) {
0594         if (filter.d->condition && d->condition->type() == AbstractCondition::And) {
0595             if (filter.d->condition->type() == AbstractCondition::And)
0596                 static_cast<And*>(d->condition)->list += copyList(static_cast<And*>(filter.d->condition)->list);
0597             else if (filter.d->condition->type() == AbstractCondition::Or)
0598                 static_cast<And*>(d->condition)->list.append(new Or(*static_cast<Or*>(filter.d->condition)));
0599             else // if (filter.d->condition->type() == AbstractCondition::Condition)
0600                 static_cast<And*>(d->condition)->list.append(new Condition(*static_cast<Condition*>(filter.d->condition)));
0601         } else if (filter.d->condition) {
0602             And* andComposition = new And();
0603             andComposition->list.append(d->condition);
0604             if (filter.d->condition->type() == AbstractCondition::And)
0605                 andComposition->list += copyList(static_cast<And*>(filter.d->condition)->list);
0606             else if (filter.d->condition->type() == AbstractCondition::Or)
0607                 andComposition->list.append(new Or(*static_cast<Or*>(filter.d->condition)));
0608             else // if (filter.d->condition->type() == AbstractCondition::Condition)
0609                 andComposition->list.append(new Condition(*static_cast<Condition*>(filter.d->condition)));
0610             d->condition = andComposition;
0611         }
0612     } else { // composition == OrComposition
0613         if (filter.d->condition && d->condition->type() == AbstractCondition::Or) {
0614             if (filter.d->condition->type() == AbstractCondition::And)
0615                 static_cast<Or*>(d->condition)->list.append(new And(*static_cast<And*>(filter.d->condition)));
0616             else if (filter.d->condition->type() == AbstractCondition::Or)
0617                 static_cast<Or*>(d->condition)->list += copyList(static_cast<Or*>(filter.d->condition)->list);
0618             else // if (filter.d->condition->type() == AbstractCondition::Condition)
0619                 static_cast<Or*>(d->condition)->list.append(new Condition(*static_cast<Condition*>(filter.d->condition)));
0620         } else if (filter.d->condition) {
0621             Or* orComposition = new Or();
0622             orComposition->list.append(d->condition);
0623             if (filter.d->condition->type() == AbstractCondition::And)
0624                 orComposition->list.append(new And(*static_cast<And*>(filter.d->condition)));
0625             else if (filter.d->condition->type() == AbstractCondition::Or)
0626                 orComposition->list += copyList(static_cast<Or*>(filter.d->condition)->list);
0627             else // if (filter.d->condition->type() == AbstractCondition::Condition)
0628                 orComposition->list.append(new Condition(*static_cast<Condition*>(filter.d->condition)));
0629             d->condition = orComposition;
0630         }
0631     }
0632 }
0633 
0634 QHash<QString, Filter::Comparison> Filter::conditions(int fieldNumber) const
0635 {
0636     return d->condition ? d->condition->conditions(fieldNumber) : QHash<QString, Comparison>();
0637 }
0638 
0639 void Filter::removeConditions(int fieldNumber)
0640 {
0641     if (fieldNumber == -1) {
0642 //         debugSheets <<"removing all conditions";
0643         delete d->condition;
0644         d->condition = 0;
0645         return;
0646     }
0647     if (!d->condition)
0648         return;
0649     d->condition->removeConditions(fieldNumber);
0650     if (d->condition->isEmpty()) {
0651         delete d->condition;
0652         d->condition = 0;
0653     }
0654 }
0655 
0656 bool Filter::isEmpty() const
0657 {
0658     return d->condition ? d->condition->isEmpty() : true;
0659 }
0660 
0661 bool Filter::evaluate(const Database& database, int index) const
0662 {
0663     return d->condition ? d->condition->evaluate(database, index) : true;
0664 }
0665 
0666 bool Filter::loadOdf(const KoXmlElement& element, const Map* map)
0667 {
0668     if (element.hasAttributeNS(KoXmlNS::table, "target-range-address")) {
0669         const QString address = element.attributeNS(KoXmlNS::table, "target-range-address", QString());
0670         // only absolute addresses allowed; no fallback sheet needed
0671         d->targetRangeAddress = Region(Odf::loadRegion(address), map);
0672         if (!d->targetRangeAddress.isValid())
0673             return false;
0674     }
0675     if (element.hasAttributeNS(KoXmlNS::table, "condition-source")) {
0676         if (element.attributeNS(KoXmlNS::table, "condition-source", "self") == "cell-range")
0677             d->conditionSource = Private::CellRange;
0678         else
0679             d->conditionSource = Private::Self;
0680     }
0681     if (element.hasAttributeNS(KoXmlNS::table, "condition-source-range-address")) {
0682         const QString address = element.attributeNS(KoXmlNS::table, "condition-source-range-address", QString());
0683         // only absolute addresses allowed; no fallback sheet needed
0684         d->conditionSourceRangeAddress = Region(Odf::loadRegion(address), map);
0685     }
0686     if (element.hasAttributeNS(KoXmlNS::table, "display-duplicates")) {
0687         if (element.attributeNS(KoXmlNS::table, "display-duplicates", "true") == "false")
0688             d->displayDuplicates = false;
0689         else
0690             d->displayDuplicates = true;
0691     }
0692     KoXmlElement conditionElement;
0693     forEachElement(conditionElement, element) {
0694         if (conditionElement.localName() == "filter-and") {
0695             d->condition = new And();
0696             break;
0697         } else if (conditionElement.localName() == "filter-or") {
0698             d->condition = new Or();
0699             break;
0700         } else if (conditionElement.localName() == "filter-condition") {
0701             d->condition = new Condition();
0702             break;
0703         }
0704     }
0705     if (!d->condition)
0706         return false;
0707     if (!d->condition->loadOdf(conditionElement.toElement())) {
0708         delete d->condition;
0709         d->condition = 0;
0710         return false;
0711     }
0712     return true;
0713 }
0714 
0715 void Filter::saveOdf(KoXmlWriter& xmlWriter) const
0716 {
0717     if (!d->condition)
0718         return;
0719     xmlWriter.startElement("table:filter");
0720     if (!d->targetRangeAddress.isEmpty())
0721         xmlWriter.addAttribute("table:target-range-address", Odf::saveRegion(d->targetRangeAddress.name()));
0722     if (d->conditionSource != Private::Self)
0723         xmlWriter.addAttribute("table:condition-source", "cell-range");
0724     if (!d->conditionSourceRangeAddress.isEmpty())
0725         xmlWriter.addAttribute("table:condition-source-range-address", Odf::saveRegion(d->conditionSourceRangeAddress.name()));
0726     if (!d->displayDuplicates)
0727         xmlWriter.addAttribute("table:display-duplicates", "false");
0728     d->condition->saveOdf(xmlWriter);
0729     xmlWriter.endElement();
0730 }
0731 
0732 bool Filter::conditionsEquals(AbstractCondition* a, AbstractCondition* b)
0733 {
0734     if (!a || !b)
0735         return a == b;
0736     if (a->type() != b->type())
0737         return false;
0738     if (a->type() == AbstractCondition::And && *static_cast<And*>(a) != *static_cast<And*>(b))
0739         return false;
0740     if (a->type() == AbstractCondition::Or && *static_cast<Or*>(a) != *static_cast<Or*>(b))
0741         return false;
0742     if (a->type() == AbstractCondition::Condition && *static_cast<Condition*>(a) != *static_cast<Condition*>(b))
0743         return false;
0744     return true;
0745 }
0746 
0747 bool Filter::operator==(const Filter& other) const
0748 {
0749     if (d->targetRangeAddress != other.d->targetRangeAddress)
0750         return false;
0751     if (d->conditionSource != other.d->conditionSource)
0752         return false;
0753     if (d->conditionSourceRangeAddress != other.d->conditionSourceRangeAddress)
0754         return false;
0755     if (d->displayDuplicates != other.d->displayDuplicates)
0756         return false;
0757     return conditionsEquals(d->condition, other.d->condition);
0758 }
0759 
0760 void Filter::dump() const
0761 {
0762     if (d->condition)
0763         debugSheets << "Condition:" + d->condition->dump();
0764     else
0765         debugSheets << "Condition: 0";
0766 }
0767 
0768 bool AbstractCondition::listsAreEqual(const QList<AbstractCondition *> &a, const QList<AbstractCondition *> &b)
0769 {
0770     if (a.size() != b.size()) return false;
0771     for (int i = 0; i < a.size(); i++) {
0772         if (!Filter::conditionsEquals(a[i], b[i]))
0773             return false;
0774     }
0775     return true;
0776 }