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