File indexing completed on 2024-05-12 15:26:38

0001 /***************************************************************************
0002     File                 : AbstractSimpleFilter.cpp
0003     Project              : AbstractColumn
0004     --------------------------------------------------------------------
0005     Copyright            : (C) 2007,2008 by Knut Franke, Tilman Benkert
0006     Email (use @ for *)  : knut.franke*gmx.de, thzs*gmx.net
0007     Copyright            : (C) 2020 Stefan Gerlach (stefan.gerlach@uni.kn)
0008     Description          : Simplified filter interface for filters with
0009                            only one output port.
0010 
0011  ***************************************************************************/
0012 
0013 /***************************************************************************
0014  *                                                                         *
0015  *  This program is free software; you can redistribute it and/or modify   *
0016  *  it under the terms of the GNU General Public License as published by   *
0017  *  the Free Software Foundation; either version 2 of the License, or      *
0018  *  (at your option) any later version.                                    *
0019  *                                                                         *
0020  *  This program is distributed in the hope that it will be useful,        *
0021  *  but WITHOUT ANY WARRANTY; without even the implied warranty of         *
0022  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          *
0023  *  GNU General Public License for more details.                           *
0024  *                                                                         *
0025  *   You should have received a copy of the GNU General Public License     *
0026  *   along with this program; if not, write to the Free Software           *
0027  *   Foundation, Inc., 51 Franklin Street, Fifth Floor,                    *
0028  *   Boston, MA  02110-1301  USA                                           *
0029  *                                                                         *
0030  ***************************************************************************/
0031 
0032 #include "AbstractSimpleFilter.h"
0033 #include "backend/lib/XmlStreamReader.h"
0034 
0035 #include <QDateTime>
0036 #include <QDate>
0037 #include <QTime>
0038 
0039 #include <KLocalizedString>
0040 
0041 /**
0042  * \class AbstractSimpleFilter
0043  * \brief Simplified filter interface for filters with only one output port.
0044  *
0045  * This class is only meant to simplify implementation of a restricted subtype of filter.
0046  * It should not be instantiated directly. You should always either derive from
0047  * AbstractFilter or (if necessary) provide an actual (non-abstract) implementation.
0048  *
0049  * The trick here is that, in a sense, the filter is its own output port. This means you
0050  * can implement a complete filter in only one class and don't have to coordinate data
0051  * transfer between a filter class and a data source class.
0052  * Additionally, AbstractSimpleFilter offers some useful convenience methods which make writing
0053  * filters as painless as possible.
0054  *
0055  * For the data type of the output, all types supported by AbstractColumn (currently double, QString and
0056  * QDateTime) are supported.
0057  *
0058  * \section tutorial1 Tutorial, Step 1
0059  * The simplest filter you can write assumes there's also only one input port and rows on the
0060  * input correspond 1:1 to rows in the output. All you need to specify is what data type you
0061  * want to have (in this example double) on the input port and how to compute the output values:
0062  *
0063  * \code
0064  * 01 #include "AbstractSimpleFilter.h"
0065  * 02 class TutorialFilter1 : public AbstractSimpleFilter
0066  * 03 {
0067  * 04   protected:
0068  * 05       virtual bool inputAcceptable(int, AbstractColumn *source) {
0069  * 06           return (source->columnMode() == AbstractColumn::Numeric);
0070  * 07       }
0071  * 08   public:
0072  * 09       virtual AbstractColumn::ColumnMode columnMode() const { return AbstractColumn::Numeric; }
0073  * 10
0074  * 11       virtual double valueAt(int row) const {
0075  * 12           if (!m_inputs.value(0)) return 0.0;
0076  * 13           double input_value = m_inputs.value(0)->valueAt(row);
0077  * 14           return input_value * input_value;
0078  * 15       }
0079  * 16 };
0080  * \endcode
0081  *
0082  * This filter reads an input value (line 13) and returns its square (line 14).
0083  * Reimplementing inputAcceptable() makes sure that the data source really is of type
0084  * double (lines 5 to 7). Otherwise, the source will be rejected by AbstractFilter::input().
0085  * The output type is reported by reimplementing columnMode() (line 09).
0086  * Before you actually use m_inputs.value(0), make sure that the input port has
0087  * been connected to a data source (line 12).
0088  * Otherwise line 13 would result in a crash. That's it, we've already written a
0089  * fully-functional filter!
0090  *
0091  * Equivalently, you can write 1:1-filters for QString or QDateTime inputs by checking for
0092  * AbstractColumn::TypeQString or AbstractColumn::TypeQDateTime in line 6. You would then use
0093  * AbstractColumn::textAt(row) or AbstractColumn::dateTimeAt(row) in line 13 to access the input data.
0094  * For QString output, you need to implement AbstractColumn::textAt(row).
0095  * For QDateTime output, you have to implement three methods:
0096  * \code
0097  * virtual QDateTime dateTimeAt(int row) const;
0098  * virtual QDate dateAt(int row) const;
0099  * virtual QTime timeAt(int row) const;
0100  * \endcode
0101  *
0102  * \section tutorial2 Tutorial, Step 2
0103  * Now for something slightly more interesting: a filter that uses only every second row of its
0104  * input. We no longer have a 1:1 correspondence between input and output rows, so we'll have
0105  * to do a bit more work in order to have everything come out as expected.
0106  * We'll use double-typed input and output again:
0107  * \code
0108  * 01 #include "AbstractSimpleFilter.h"
0109  * 02 class TutorialFilter2 : public AbstractSimpleFilter
0110  * 03 {
0111  * 04   protected:
0112  * 05       virtual bool inputAcceptable(int, AbstractColumn *source) {
0113  * 06           return (source->columnMode() == AbstractColumn::Numeric);
0114  * 07       }
0115  * 08   public:
0116  * 09       virtual AbstractColumn::ColumnMode columnMode() const { return AbstractColumn::Numeric; }
0117  * \endcode
0118  * Even rows (including row 0) get dropped, odd rows are renumbered:
0119  * \code
0120  * 10   public:
0121  * 11   virtual double valueAt(int row) const {
0122  * 12       if (!m_inputs.value(0)) return 0.0;
0123  * 13       return m_inputs.value(0)->valueAt(2*row + 1);
0124  * 14   }
0125  * \endcode
0126  */
0127 
0128 // TODO: should simple filters have a name argument?
0129 /**
0130  * \brief Ctor
0131  */
0132 AbstractSimpleFilter::AbstractSimpleFilter()
0133     : AbstractFilter("SimpleFilter"), m_output_column(new SimpleFilterColumn(this)) {
0134     addChildFast(m_output_column);
0135 }
0136 
0137 /**
0138  * \brief Default to one input port.
0139  */
0140 int AbstractSimpleFilter::inputCount() const {
0141     return 1;
0142 }
0143 
0144 /**
0145  * \brief We manage only one output port (don't override unless you really know what you are doing).
0146  */
0147 int AbstractSimpleFilter::outputCount() const {
0148     return 1;
0149 }
0150 
0151 /**
0152  * \brief Copy plot designation of input port 0.
0153  */
0154 AbstractColumn::PlotDesignation AbstractSimpleFilter::plotDesignation() const {
0155     return m_inputs.value(0) ? m_inputs.at(0)->plotDesignation() : AbstractColumn::PlotDesignation::NoDesignation;
0156 }
0157 
0158 /**
0159  * \brief Copy plot designation string of input port 0.
0160  */
0161 QString AbstractSimpleFilter::plotDesignationString() const {
0162     return m_inputs.value(0) ? m_inputs.at(0)->plotDesignationString() : QString("");
0163 }
0164 
0165 /**
0166  * \brief Return the column mode
0167  *
0168  * This function is most used by tables but can also be used
0169  * by plots. The column mode specifies how to interpret
0170  * the values in the column additional to the data type.
0171  */
0172 AbstractColumn::ColumnMode AbstractSimpleFilter::columnMode() const {
0173     // calling this function while m_input is empty is a sign of very bad code
0174     // nevertheless it will return some rather meaningless value to
0175     // avoid crashes
0176     return m_inputs.value(0) ? m_inputs.at(0)->columnMode() : AbstractColumn::ColumnMode::Text;
0177 }
0178 
0179 /**
0180  * \brief Return the content of row 'row'.
0181  *
0182  * Use this only when columnMode() is Text
0183  */
0184 QString AbstractSimpleFilter::textAt(int row) const {
0185     return m_inputs.value(0) ? m_inputs.at(0)->textAt(row) : QString();
0186 }
0187 
0188 /**
0189  * \brief Return the date part of row 'row'
0190  *
0191  * Use this only when columnMode() is DateTime, Month or Day
0192  */
0193 QDate AbstractSimpleFilter::dateAt(int row) const {
0194     return m_inputs.value(0) ? m_inputs.at(0)->dateAt(row) : QDate();
0195 }
0196 
0197 /**
0198  * \brief Return the time part of row 'row'
0199  *
0200  * Use this only when columnMode() is DateTime, Month or Day
0201  */
0202 QTime AbstractSimpleFilter::timeAt(int row) const {
0203     return m_inputs.value(0) ? m_inputs.at(0)->timeAt(row) : QTime();
0204 }
0205 
0206 /**
0207  * \brief Set the content of row 'row'
0208  *
0209  * Use this only when columnMode() is DateTime, Month or Day
0210  */
0211 QDateTime AbstractSimpleFilter::dateTimeAt(int row) const {
0212     return m_inputs.value(0) ? m_inputs.at(0)->dateTimeAt(row) : QDateTime();
0213 }
0214 
0215 /**
0216  * \brief Return the double value in row 'row'
0217  *
0218  * Use this only when columnMode() is Numeric
0219  */
0220 double AbstractSimpleFilter::valueAt(int row) const {
0221     return m_inputs.value(0) ? m_inputs.at(0)->valueAt(row) : 0.0;
0222 }
0223 
0224 /**
0225  * \brief Return the integer value in row 'row'
0226  *
0227  * Use this only when columnMode() is Integer
0228  */
0229 int AbstractSimpleFilter::integerAt(int row) const {
0230     return m_inputs.value(0) ? m_inputs.at(0)->integerAt(row) : 0;
0231 }
0232 
0233 /**
0234  * \brief Return the bigint value in row 'row'
0235  *
0236  * Use this only when columnMode() is BigInt
0237  */
0238 qint64 AbstractSimpleFilter::bigIntAt(int row) const {
0239     return m_inputs.value(0) ? m_inputs.at(0)->bigIntAt(row) : 0;
0240 }
0241 
0242 /**
0243  * \brief Number of output rows == number of input rows
0244  *
0245  * ... unless overridden in a subclass.
0246  */
0247 int AbstractSimpleFilter::rowCount() const {
0248     return m_inputs.value(0) ? m_inputs.at(0)->rowCount() : 0;
0249 }
0250 
0251 /**
0252  * \brief Number of output rows == number of input rows
0253  *
0254  * ... unless overridden in a subclass.
0255  */
0256 int AbstractSimpleFilter::availableRowCount() const {
0257     return m_inputs.value(0) ? m_inputs.at(0)->availableRowCount() : 0;
0258 }
0259 
0260 /**
0261  * \brief Rows that will change when the given input interval changes.
0262  *
0263  * This implementation assumes a 1:1 correspondence between input and output rows, but can be
0264  * overridden in subclasses.
0265  */
0266 QList< Interval<int> > AbstractSimpleFilter::dependentRows(const Interval<int>& inputRange) const {
0267     return QList< Interval<int> >() << inputRange;
0268 }
0269 
0270 ////////////////////////////////////////////////////////////////////////////////////////////////////
0271 //!\name signal handlers
0272 //@{
0273 ////////////////////////////////////////////////////////////////////////////////////////////////////
0274 
0275 void AbstractSimpleFilter::inputPlotDesignationAboutToChange(const AbstractColumn*) {
0276     emit m_output_column->plotDesignationAboutToChange(m_output_column);
0277 }
0278 
0279 void AbstractSimpleFilter::inputPlotDesignationChanged(const AbstractColumn*) {
0280     emit m_output_column->plotDesignationChanged(m_output_column);
0281 }
0282 
0283 void AbstractSimpleFilter::inputModeAboutToChange(const AbstractColumn*) {
0284     emit m_output_column->dataAboutToChange(m_output_column);
0285 }
0286 
0287 void AbstractSimpleFilter::inputModeChanged(const AbstractColumn*) {
0288     emit m_output_column->dataChanged(m_output_column);
0289 }
0290 
0291 void AbstractSimpleFilter::inputDataAboutToChange(const AbstractColumn*) {
0292     emit m_output_column->dataAboutToChange(m_output_column);
0293 }
0294 
0295 void AbstractSimpleFilter::inputDataChanged(const AbstractColumn*) {
0296     emit m_output_column->dataChanged(m_output_column);
0297 }
0298 
0299 void AbstractSimpleFilter::inputRowsAboutToBeInserted(const AbstractColumn * source, int before, int count) {
0300     Q_UNUSED(source);
0301     Q_UNUSED(count);
0302     foreach (const Interval<int>& output_range, dependentRows(Interval<int>(before, before)))
0303         emit m_output_column->rowsAboutToBeInserted(m_output_column, output_range.start(), output_range.size());
0304 }
0305 
0306 void AbstractSimpleFilter::inputRowsInserted(const AbstractColumn * source, int before, int count) {
0307     Q_UNUSED(source);
0308     Q_UNUSED(count);
0309     foreach (const Interval<int>& output_range, dependentRows(Interval<int>(before, before)))
0310         emit m_output_column->rowsInserted(m_output_column, output_range.start(), output_range.size());
0311 }
0312 
0313 void AbstractSimpleFilter::inputRowsAboutToBeRemoved(const AbstractColumn * source, int first, int count) {
0314     Q_UNUSED(source);
0315     foreach (const Interval<int>& output_range, dependentRows(Interval<int>(first, first+count-1)))
0316         emit m_output_column->rowsAboutToBeRemoved(m_output_column, output_range.start(), output_range.size());
0317 }
0318 
0319 void AbstractSimpleFilter::inputRowsRemoved(const AbstractColumn * source, int first, int count) {
0320     Q_UNUSED(source);
0321     foreach (const Interval<int>& output_range, dependentRows(Interval<int>(first, first+count-1)))
0322         emit m_output_column->rowsRemoved(m_output_column, output_range.start(), output_range.size());
0323 }
0324 
0325 ////////////////////////////////////////////////////////////////////////////////////////////////////
0326 //@}
0327 ////////////////////////////////////////////////////////////////////////////////////////////////////
0328 
0329 /**
0330  * \brief Return a pointer to #m_output_column on port 0 (don't override unless you really know what you are doing).
0331  */
0332 AbstractColumn *AbstractSimpleFilter::output(int port) {
0333     return port == 0 ? static_cast<AbstractColumn*>(m_output_column) : nullptr;
0334 }
0335 
0336 const AbstractColumn *AbstractSimpleFilter::output(int port) const {
0337     return port == 0 ? static_cast<const AbstractColumn*>(m_output_column) : nullptr;
0338 }
0339 
0340 ////////////////////////////////////////////////////////////////////////////////////////////////////
0341 //! \name serialize/deserialize
0342 //@{
0343 ////////////////////////////////////////////////////////////////////////////////////////////////////
0344 
0345 /**
0346  * \brief Save to XML
0347  */
0348 void AbstractSimpleFilter::save(QXmlStreamWriter * writer) const {
0349     writer->writeStartElement("simple_filter");
0350     writeBasicAttributes(writer);
0351     writeExtraAttributes(writer);
0352     writer->writeAttribute("filter_name", metaObject()->className());
0353     writeCommentElement(writer);
0354     writer->writeEndElement();
0355 }
0356 
0357 /**
0358  * \brief Override this in derived classes if they have other attributes than filter_name
0359  */
0360 void AbstractSimpleFilter::writeExtraAttributes(QXmlStreamWriter * writer) const {
0361     Q_UNUSED(writer)
0362 }
0363 
0364 ////////////////////////////////////////////////////////////////////////////////////////////////////
0365 //@}
0366 ////////////////////////////////////////////////////////////////////////////////////////////////////
0367 
0368 /**
0369  * \brief Load from XML
0370  */
0371 bool AbstractSimpleFilter::load(XmlStreamReader* reader, bool preview) {
0372     Q_UNUSED(preview); //TODO
0373 
0374     if (!readBasicAttributes(reader))
0375         return false;
0376 
0377     QXmlStreamAttributes attribs = reader->attributes();
0378     QString str = attribs.value(reader->namespaceUri().toString(), "filter_name").toString();
0379     if (str != metaObject()->className()) {
0380         reader->raiseError(i18n("incompatible filter type"));
0381         return false;
0382     }
0383 
0384     // read child elements
0385     while (!reader->atEnd()) {
0386         reader->readNext();
0387 
0388         if (reader->isEndElement()) break;
0389 
0390         if (reader->isStartElement()) {
0391             if (reader->name() == "comment") {
0392                 if (!readCommentElement(reader)) return false;
0393             } else { // unknown element
0394                 reader->raiseWarning(i18n("unknown element '%1'", reader->name().toString()));
0395                 if (!reader->skipToEndElement()) return false;
0396             }
0397         }
0398     }
0399 
0400     return !reader->hasError();
0401 }
0402 
0403 ////////////////////////////////////////////////////////////////////////////////////////////////////
0404 //! \class SimpleFilterColumn
0405 ////////////////////////////////////////////////////////////////////////////////////////////////////
0406 
0407 AbstractColumn::ColumnMode SimpleFilterColumn::columnMode() const {
0408     return m_owner->columnMode();
0409 }
0410 
0411 QString SimpleFilterColumn::textAt(int row) const {
0412     return m_owner->textAt(row);
0413 }
0414 
0415 QDate SimpleFilterColumn::dateAt(int row) const {
0416     return m_owner->dateAt(row);
0417 }
0418 
0419 QTime SimpleFilterColumn::timeAt(int row) const {
0420     return m_owner->timeAt(row);
0421 }
0422 
0423 QDateTime SimpleFilterColumn::dateTimeAt(int row) const {
0424     return m_owner->dateTimeAt(row);
0425 }
0426 
0427 double SimpleFilterColumn::valueAt(int row) const {
0428     return m_owner->valueAt(row);
0429 }
0430 
0431 int SimpleFilterColumn::integerAt(int row) const {
0432     return m_owner->integerAt(row);
0433 }
0434 
0435 qint64 SimpleFilterColumn::bigIntAt(int row) const {
0436     return m_owner->bigIntAt(row);
0437 }