File indexing completed on 2024-06-16 03:42:49

0001 /*
0002     File                 : ReadStatFilter.cpp
0003     Project              : LabPlot
0004     Description          : ReadStat I/O-filter
0005     --------------------------------------------------------------------
0006     SPDX-FileCopyrightText: 2021 Stefan Gerlach <stefan.gerlach@uni.kn>
0007     SPDX-License-Identifier: GPL-2.0-or-later
0008 */
0009 #include "ReadStatFilter.h"
0010 #include "ReadStatFilterPrivate.h"
0011 #include "backend/core/column/Column.h"
0012 #include "backend/lib/XmlStreamReader.h"
0013 #include "backend/lib/macros.h"
0014 #include "backend/spreadsheet/Spreadsheet.h"
0015 
0016 #include <KLocalizedString>
0017 #include <QDateTime>
0018 #include <QFile>
0019 
0020 ///////////// macros ///////////////////////////////////////////////
0021 
0022 //////////////////////////////////////////////////////////////////////
0023 
0024 /*!
0025     \class ReadStatFilter
0026     \brief Manages the import/export of data from/to a ReadStat file.
0027 
0028     \ingroup datasources
0029 */
0030 ReadStatFilter::ReadStatFilter()
0031     : AbstractFileFilter(FileType::READSTAT)
0032     , d(new ReadStatFilterPrivate(this)) {
0033 }
0034 
0035 ReadStatFilter::~ReadStatFilter() = default;
0036 
0037 QVector<QStringList> ReadStatFilter::preview(const QString& fileName, int lines) {
0038     return d->preview(fileName, lines);
0039 }
0040 
0041 /*!
0042   reads the content of the file \c fileName to the data source \c dataSource.
0043 */
0044 void ReadStatFilter::readDataFromFile(const QString& fileName, AbstractDataSource* dataSource, AbstractFileFilter::ImportMode mode) {
0045     d->readDataFromFile(fileName, dataSource, mode);
0046 }
0047 
0048 /*!
0049 writes the content of the data source \c dataSource to the file \c fileName.
0050 */
0051 void ReadStatFilter::write(const QString& fileName, AbstractDataSource* dataSource) {
0052     d->write(fileName, dataSource);
0053     // TODO: not implemented yet
0054 }
0055 
0056 void ReadStatFilter::setStartRow(const int r) {
0057     d->startRow = r;
0058 }
0059 int ReadStatFilter::startRow() const {
0060     return d->startRow;
0061 }
0062 
0063 void ReadStatFilter::setEndRow(const int r) {
0064     d->endRow = r;
0065 }
0066 int ReadStatFilter::endRow() const {
0067     return d->endRow;
0068 }
0069 
0070 void ReadStatFilter::setStartColumn(const int c) {
0071     d->startColumn = c;
0072 }
0073 int ReadStatFilter::startColumn() const {
0074     return d->startColumn;
0075 }
0076 
0077 void ReadStatFilter::setEndColumn(const int c) {
0078     d->endColumn = c;
0079 }
0080 int ReadStatFilter::endColumn() const {
0081     return d->endColumn;
0082 }
0083 
0084 QStringList ReadStatFilter::vectorNames() const {
0085     return d->varNames;
0086 }
0087 
0088 QVector<AbstractColumn::ColumnMode> ReadStatFilter::columnModes() const {
0089     return d->columnModes;
0090 }
0091 
0092 ///////////////////////////////////////////////////////////////////////
0093 #ifdef HAVE_READSTAT
0094 int ReadStatFilter::getMetaData(readstat_metadata_t* metadata, void* md) {
0095     *(readstat_metadata_t*)md = *metadata;
0096 
0097     return READSTAT_HANDLER_OK;
0098 }
0099 #endif
0100 
0101 QString ReadStatFilter::fileInfoString(const QString& fileName) {
0102     DEBUG(Q_FUNC_INFO << ", file name = " << qPrintable(fileName))
0103 
0104     QString info;
0105 #ifdef HAVE_READSTAT
0106     readstat_parser_t* parser = readstat_parser_init();
0107     readstat_set_metadata_handler(parser, &getMetaData);
0108 
0109     readstat_error_t error = READSTAT_OK;
0110     readstat_metadata_t metadata;
0111     if (fileName.endsWith(QLatin1String(".dta")))
0112         error = readstat_parse_dta(parser, qPrintable(fileName), &metadata);
0113     else if (fileName.endsWith(QLatin1String(".sav")) || fileName.endsWith(QLatin1String(".zsav")))
0114         error = readstat_parse_sav(parser, qPrintable(fileName), &metadata);
0115     else if (fileName.endsWith(QLatin1String(".por")))
0116         error = readstat_parse_por(parser, qPrintable(fileName), &metadata);
0117     else if (fileName.endsWith(QLatin1String(".sas7bdat")))
0118         error = readstat_parse_sas7bdat(parser, qPrintable(fileName), &metadata);
0119     else if (fileName.endsWith(QLatin1String(".sas7bcat")))
0120         error = readstat_parse_sas7bcat(parser, qPrintable(fileName), &metadata);
0121     else if (fileName.endsWith(QLatin1String(".xpt")) || fileName.endsWith(QLatin1String(".xpt5")) || fileName.endsWith(QLatin1String(".xpt8")))
0122         error = readstat_parse_xport(parser, qPrintable(fileName), &metadata);
0123     else
0124         return i18n("Unknown file extension");
0125 
0126     readstat_parser_free(parser);
0127 
0128     if (error == READSTAT_OK) {
0129         info += i18n("Number of records: %1", QString::number((int64_t)metadata.row_count));
0130         info += QLatin1String("<br>");
0131         info += i18n("Number of variables: %1", QString::number((int64_t)metadata.var_count));
0132         info += QLatin1String("<br>");
0133         info += i18n("Creation time: %1", QDateTime::fromSecsSinceEpoch(metadata.creation_time).toString());
0134         info += QLatin1String("<br>");
0135         info += i18n("Modification time: %1", QDateTime::fromSecsSinceEpoch(metadata.modified_time).toString());
0136         info += QLatin1String("<br>");
0137         info += i18n("Format version: %1", QString::number((int64_t)metadata.file_format_version));
0138         info += QLatin1String("<br>");
0139         QString compress;
0140         switch (metadata.compression) {
0141         case READSTAT_COMPRESS_NONE:
0142             compress = QStringLiteral("none");
0143             break;
0144         case READSTAT_COMPRESS_ROWS:
0145             compress = QStringLiteral("rows");
0146             break;
0147         case READSTAT_COMPRESS_BINARY:
0148             compress = QStringLiteral("binary");
0149             break;
0150         }
0151         info += i18n("Compression: %1", compress);
0152         info += QStringLiteral("<br>");
0153         QString endian;
0154         switch (metadata.endianness) {
0155         case READSTAT_ENDIAN_NONE:
0156             endian = QStringLiteral("none");
0157             break;
0158         case READSTAT_ENDIAN_LITTLE:
0159             endian = QStringLiteral("little");
0160             break;
0161         case READSTAT_ENDIAN_BIG:
0162             endian = QStringLiteral("big");
0163             break;
0164         }
0165         info += i18n("Endianess: %1", endian);
0166         info += QLatin1String("<br>");
0167         info += i18n("Table name: %1", QLatin1String(metadata.table_name));
0168         info += QLatin1String("<br>");
0169         info += i18n("File label: %1", QLatin1String(metadata.file_label));
0170         info += QLatin1String("<br>");
0171         info += i18n("File encoding: %1", QLatin1String(metadata.file_encoding));
0172         info += QLatin1String("<br>");
0173         info += i18n("64bit: %1", QString::number((unsigned int)metadata.is64bit));
0174         info += QLatin1String("<br>");
0175     } else {
0176         info += i18n("Error getting file info");
0177     }
0178 #endif
0179 
0180     return info;
0181 }
0182 
0183 // #####################################################################
0184 // ################### Private implementation ##########################
0185 // #####################################################################
0186 
0187 // static members (needed by C callbacks)
0188 int ReadStatFilterPrivate::m_varCount = 0;
0189 int ReadStatFilterPrivate::m_rowCount = 0;
0190 QStringList ReadStatFilterPrivate::varNames;
0191 QVector<AbstractColumn::ColumnMode> ReadStatFilterPrivate::columnModes;
0192 QStringList ReadStatFilterPrivate::m_lineString;
0193 QVector<QStringList> ReadStatFilterPrivate::dataStrings;
0194 std::vector<void*> ReadStatFilterPrivate::m_dataContainer;
0195 QStringList ReadStatFilterPrivate::m_notes;
0196 QVector<QString> ReadStatFilterPrivate::m_valueLabels;
0197 QMap<QString, LabelSet> ReadStatFilterPrivate::m_labelSets;
0198 int ReadStatFilterPrivate::startRow{1}, ReadStatFilterPrivate::endRow{-1};
0199 int ReadStatFilterPrivate::startColumn{1}, ReadStatFilterPrivate::endColumn{-1};
0200 
0201 #ifdef HAVE_READSTAT
0202 // callbacks
0203 int ReadStatFilterPrivate::getMetaData(readstat_metadata_t* metadata, void*) {
0204     DEBUG(Q_FUNC_INFO)
0205     m_varCount = readstat_get_var_count(metadata);
0206     m_rowCount = readstat_get_row_count(metadata);
0207     m_valueLabels.resize(m_varCount);
0208 
0209     return READSTAT_HANDLER_OK;
0210 }
0211 int ReadStatFilterPrivate::getVarName(int /*index*/, readstat_variable_t* variable, const char* val_labels, void*) {
0212     // DEBUG(Q_FUNC_INFO)
0213 
0214     // only on column from startColumn to endColumn
0215     const int col = readstat_variable_get_index(variable);
0216     if (col < startColumn - 1 || (endColumn != -1 && col > endColumn - 1)) {
0217         // DEBUG(Q_FUNC_INFO << ", out of range row/col "<< index << " / " << col)
0218         return READSTAT_HANDLER_OK;
0219     }
0220 
0221     if (val_labels) {
0222         DEBUG(Q_FUNC_INFO << ", val_labels of col " << col << " : " << val_labels)
0223         m_valueLabels[col] = QLatin1String(val_labels);
0224         varNames << QLatin1String(readstat_variable_get_name(variable)) + QStringLiteral(" : ") + QLatin1String(val_labels);
0225     } else
0226         varNames << QLatin1String(readstat_variable_get_name(variable));
0227 
0228     return READSTAT_HANDLER_OK;
0229 }
0230 int ReadStatFilterPrivate::getColumnModes(int row, readstat_variable_t* variable, readstat_value_t value, void*) {
0231     if (row >= m_rowCount) // more rows found than meta data said it has (like -1)
0232         m_rowCount = row + 1;
0233 
0234     const int col = readstat_variable_get_index(variable);
0235     if (row >= startRow || col < startColumn - 1 || (endColumn != -1 && col > endColumn - 1)) // run only on first row and selected cols
0236         return READSTAT_HANDLER_OK;
0237 
0238     // column modes
0239     switch (value.type) {
0240     case READSTAT_TYPE_INT8:
0241     case READSTAT_TYPE_INT16:
0242     case READSTAT_TYPE_INT32:
0243         columnModes << AbstractColumn::ColumnMode::Integer;
0244         break;
0245     case READSTAT_TYPE_FLOAT:
0246     case READSTAT_TYPE_DOUBLE:
0247         columnModes << AbstractColumn::ColumnMode::Double;
0248         break;
0249     case READSTAT_TYPE_STRING:
0250     case READSTAT_TYPE_STRING_REF:
0251         columnModes << AbstractColumn::ColumnMode::Text;
0252     }
0253 
0254     return READSTAT_HANDLER_OK;
0255 }
0256 int ReadStatFilterPrivate::getValuesPreview(int row, readstat_variable_t* variable, readstat_value_t value, void* ptr) {
0257     // DEBUG(Q_FUNC_INFO << ", start/end row =" << startRow << "/" << endRow)
0258 
0259     // read only from start to end row/column
0260     const int col = readstat_variable_get_index(variable);
0261     if (row < startRow - 1 || (endRow != -1 && row > endRow - 1) || col < startColumn - 1 || (endColumn != -1 && col > endColumn - 1)) {
0262         // DEBUG(Q_FUNC_INFO << ", out of range row/col "<< row << " / " << col)
0263         return READSTAT_HANDLER_OK;
0264     }
0265 
0266     if (row == startRow - 1)
0267         getColumnModes(row, variable, value, ptr);
0268 
0269     // read values into m_lineString and finally into dataStrings
0270     if (col == startColumn - 1)
0271         m_lineString.clear();
0272 
0273     if (value.is_system_missing) {
0274         m_lineString << QString();
0275     } else {
0276         switch (value.type) {
0277         case READSTAT_TYPE_INT8:
0278             m_lineString << QString::number(readstat_int8_value(value));
0279             break;
0280         case READSTAT_TYPE_INT16:
0281             m_lineString << QString::number(readstat_int16_value(value));
0282             break;
0283         case READSTAT_TYPE_INT32:
0284             m_lineString << QString::number(readstat_int32_value(value));
0285             break;
0286         case READSTAT_TYPE_FLOAT:
0287             m_lineString << QString::number(readstat_float_value(value));
0288             break;
0289         case READSTAT_TYPE_DOUBLE:
0290             m_lineString << QString::number(readstat_double_value(value));
0291             break;
0292         case READSTAT_TYPE_STRING:
0293         case READSTAT_TYPE_STRING_REF:
0294             m_lineString << QLatin1String(readstat_string_value(value));
0295         }
0296     }
0297 
0298     if (col == m_varCount - 1 || (endColumn != -1 && col == endColumn - 1)) {
0299         // QDEBUG(Q_FUNC_INFO << ", data line = " << m_lineString)
0300         dataStrings << m_lineString;
0301     }
0302 
0303     return READSTAT_HANDLER_OK;
0304 }
0305 int ReadStatFilterPrivate::getValues(int row, readstat_variable_t* variable, readstat_value_t value, void*) {
0306     // only read from start to end row/col
0307     const int col = readstat_variable_get_index(variable);
0308     if (row < startRow - 1 || (endRow != -1 && row > endRow - 1) || col < startColumn - 1 || (endColumn != -1 && col > endColumn - 1)) {
0309         // DEBUG(Q_FUNC_INFO << ", out of range row/col "<< row << " / " << col)
0310         return READSTAT_HANDLER_OK;
0311     }
0312     const int rowIndex = row - startRow + 1;
0313     const int colIndex = col - startColumn + 1;
0314 
0315     // DEBUG(Q_FUNC_INFO << ", row/col = " << row << " / " << col << ", row/col index = " << rowIndex << " / " << colIndex)
0316 
0317     // import data
0318     if (value.is_system_missing) { // empty
0319         if (value.type == READSTAT_TYPE_FLOAT || value.type == READSTAT_TYPE_DOUBLE) {
0320             QVector<double>& container = *static_cast<QVector<double>*>(m_dataContainer[colIndex]);
0321             container[rowIndex] = NAN;
0322         }
0323     } else {
0324         switch (value.type) {
0325         case READSTAT_TYPE_INT8: {
0326             QVector<int>& container = *static_cast<QVector<int>*>(m_dataContainer[colIndex]);
0327             container[rowIndex] = readstat_int8_value(value);
0328             break;
0329         }
0330         case READSTAT_TYPE_INT16: {
0331             QVector<int>& container = *static_cast<QVector<int>*>(m_dataContainer[colIndex]);
0332             container[rowIndex] = readstat_int16_value(value);
0333             break;
0334         }
0335         case READSTAT_TYPE_INT32: {
0336             QVector<int>& container = *static_cast<QVector<int>*>(m_dataContainer[colIndex]);
0337             container[rowIndex] = readstat_int32_value(value);
0338             break;
0339         }
0340         case READSTAT_TYPE_FLOAT: {
0341             QVector<double>& container = *static_cast<QVector<double>*>(m_dataContainer[colIndex]);
0342             container[rowIndex] = readstat_float_value(value);
0343             break;
0344         }
0345         case READSTAT_TYPE_DOUBLE: {
0346             QVector<double>& container = *static_cast<QVector<double>*>(m_dataContainer[colIndex]);
0347             container[rowIndex] = readstat_double_value(value);
0348             break;
0349         }
0350         case READSTAT_TYPE_STRING:
0351         case READSTAT_TYPE_STRING_REF: {
0352             QVector<QString>& container = *static_cast<QVector<QString>*>(m_dataContainer[colIndex]);
0353             container[rowIndex] = QLatin1String(readstat_string_value(value));
0354         }
0355         }
0356     }
0357 
0358     return READSTAT_HANDLER_OK;
0359 }
0360 int ReadStatFilterPrivate::getNotes(int index, const char* note, void*) {
0361     Q_UNUSED(index)
0362     DEBUG(Q_FUNC_INFO << " note " << index << ": " << note)
0363     m_notes << QLatin1String(note);
0364 
0365     return READSTAT_HANDLER_OK;
0366 }
0367 int ReadStatFilterPrivate::getFWeights(readstat_variable_t* /*var*/, void*) {
0368     // TODO: not used yet
0369     // const int col = readstat_variable_get_index(var);
0370     // DEBUG(Q_FUNC_INFO << ", fweight of col " << col)
0371 
0372     return READSTAT_HANDLER_OK;
0373 }
0374 // value labels are read in getVarName() and assigned here
0375 int ReadStatFilterPrivate::getValueLabels(const char* val_label, readstat_value_t value, const char* label, void*) {
0376     // see https://github.com/tidyverse/haven/blob/master/src/DfReader.cpp
0377     DEBUG(Q_FUNC_INFO << ", value label = " << val_label << " label = " << label << ", type = " << value.type)
0378 
0379     LabelSet& labelSet = m_labelSets[QLatin1String(val_label)];
0380     switch (value.type) {
0381     case READSTAT_TYPE_STRING:
0382     case READSTAT_TYPE_STRING_REF:
0383         // DEBUG(Q_FUNC_INFO << ", string value label")
0384         labelSet.add(QLatin1String(readstat_string_value(value)), QLatin1String(label));
0385         break;
0386     case READSTAT_TYPE_INT8:
0387         // DEBUG(Q_FUNC_INFO << ", int8 value label")
0388         labelSet.add(readstat_int8_value(value), QLatin1String(label));
0389         break;
0390     case READSTAT_TYPE_INT16:
0391         // DEBUG(Q_FUNC_INFO << ", int16 value label")
0392         labelSet.add(readstat_int16_value(value), QLatin1String(label));
0393         break;
0394     case READSTAT_TYPE_INT32:
0395         // DEBUG(Q_FUNC_INFO << ", int32 value label")
0396         labelSet.add(readstat_int32_value(value), QLatin1String(label));
0397         break;
0398     case READSTAT_TYPE_FLOAT:
0399         // DEBUG(Q_FUNC_INFO << ", float value label")
0400         labelSet.add(readstat_float_value(value), QLatin1String(label));
0401         break;
0402     case READSTAT_TYPE_DOUBLE:
0403         // DEBUG(Q_FUNC_INFO << ", double value label")
0404         labelSet.add(readstat_double_value(value), QLatin1String(label));
0405         break;
0406     }
0407 
0408     return READSTAT_HANDLER_OK;
0409 }
0410 #endif
0411 
0412 ReadStatFilterPrivate::ReadStatFilterPrivate(ReadStatFilter*) {
0413 }
0414 
0415 #ifdef HAVE_READSTAT
0416 /*!
0417  * parse the file with name fileName
0418  */
0419 readstat_error_t ReadStatFilterPrivate::parse(const QString& fileName, bool preview, bool prepare) {
0420     DEBUG(Q_FUNC_INFO << ", file " << STDSTRING(fileName) << ", start/end row: " << startRow << "/" << endRow)
0421     m_labelSets.clear();
0422 
0423     readstat_parser_t* parser = readstat_parser_init();
0424     readstat_set_metadata_handler(parser, &getMetaData); // metadata
0425     readstat_set_variable_handler(parser, &getVarName); // header
0426     if (preview) // get data and save into dataStrings
0427         readstat_set_value_handler(parser, &getValuesPreview);
0428     else if (prepare) // only read column modes
0429         readstat_set_value_handler(parser, &getColumnModes);
0430     else { // get and save data into data container
0431         readstat_set_value_handler(parser, &getValues);
0432         readstat_set_note_handler(parser, &getNotes);
0433     }
0434     readstat_set_fweight_handler(parser, &getFWeights);
0435     readstat_set_value_label_handler(parser, &getValueLabels);
0436 
0437     readstat_error_t error = READSTAT_OK;
0438     if (fileName.endsWith(QLatin1String(".dta")))
0439         error = readstat_parse_dta(parser, qPrintable(fileName), nullptr);
0440     else if (fileName.endsWith(QLatin1String(".sav")) || fileName.endsWith(QLatin1String(".zsav")))
0441         error = readstat_parse_sav(parser, qPrintable(fileName), nullptr);
0442     else if (fileName.endsWith(QLatin1String(".por")))
0443         error = readstat_parse_por(parser, qPrintable(fileName), nullptr);
0444     else if (fileName.endsWith(QLatin1String(".sas7bdat")))
0445         error = readstat_parse_sas7bdat(parser, qPrintable(fileName), nullptr);
0446     else if (fileName.endsWith(QLatin1String(".sas7bcat")))
0447         error = readstat_parse_sas7bcat(parser, qPrintable(fileName), nullptr);
0448     else if (fileName.endsWith(QLatin1String(".xpt")) || fileName.endsWith(QLatin1String(".xpt5")) || fileName.endsWith(QLatin1String(".xpt8")))
0449         error = readstat_parse_xport(parser, qPrintable(fileName), nullptr);
0450     else {
0451         DEBUG(Q_FUNC_INFO << ", ERROR: Unknown file extension")
0452     }
0453     readstat_parser_free(parser);
0454 
0455     return error;
0456 }
0457 #endif
0458 
0459 /*!
0460  * generates the preview for the file \c fileName reading the provided number of \c lines.
0461  */
0462 QVector<QStringList> ReadStatFilterPrivate::preview(const QString& fileName, int lines) {
0463     // set max. number of lines to preview by setting endRow
0464     if (endRow == -1 || endRow > startRow + lines - 1)
0465         endRow = startRow + lines - 1;
0466 
0467     varNames.clear();
0468     columnModes.clear();
0469     dataStrings.clear();
0470 
0471 #ifdef HAVE_READSTAT
0472     readstat_error_t error = parse(fileName, true); // lines?
0473 
0474     if (error == READSTAT_OK) {
0475         DEBUG(Q_FUNC_INFO << ", var count = " << m_varCount)
0476         QDEBUG(Q_FUNC_INFO << ", var names = " << varNames)
0477         for (int i = 0; i < columnModes.size(); i++)
0478             DEBUG(Q_FUNC_INFO << ", column mode " << i << " = " << ENUM_TO_STRING(AbstractColumn, ColumnMode, columnModes.at(i)))
0479         DEBUG(Q_FUNC_INFO << ", read " << dataStrings.size() << " lines")
0480     } else {
0481         DEBUG(Q_FUNC_INFO << ", ERROR: processing " << qPrintable(fileName))
0482     }
0483 #else
0484     Q_UNUSED(fileName)
0485 #endif
0486 
0487     return dataStrings;
0488 }
0489 
0490 /*!
0491     reads the content of file \c fileName to the data source \c dataSource.
0492     Uses the settings defined in the data source.
0493 */
0494 void ReadStatFilterPrivate::readDataFromFile(const QString& fileName, AbstractDataSource* dataSource, AbstractFileFilter::ImportMode mode) {
0495     DEBUG(Q_FUNC_INFO << ", fileName = \'" << STDSTRING(fileName) << "\', dataSource = " << dataSource
0496                       << ", mode = " << ENUM_TO_STRING(AbstractFileFilter, ImportMode, mode));
0497 
0498     varNames.clear();
0499     columnModes.clear();
0500     dataStrings.clear();
0501     m_valueLabels.clear();
0502     m_notes.clear();
0503 
0504 #ifdef HAVE_READSTAT
0505     DEBUG(Q_FUNC_INFO << ", Parsing meta data ...")
0506     // parse meta data and column modes only
0507     readstat_error_t error = parse(fileName, false, true);
0508     if (error != READSTAT_OK) {
0509         DEBUG(Q_FUNC_INFO << ", ERROR preparsing file " << STDSTRING(fileName))
0510         return;
0511     }
0512 
0513     DEBUG(Q_FUNC_INFO << ", found " << m_varCount << " cols, " << m_rowCount << " rows")
0514 
0515     // prepare data container
0516     const int actualEndRow = (endRow == -1 || endRow > m_rowCount) ? m_rowCount : endRow;
0517     const int actualRows = actualEndRow - startRow + 1;
0518     const int actualEndColumn = (endColumn == -1 || endColumn > m_varCount) ? m_varCount : endColumn;
0519     const int actualCols = actualEndColumn - startColumn + 1;
0520     DEBUG(Q_FUNC_INFO << ", actual cols/rows = " << actualCols << " / " << actualRows)
0521     const int columnOffset = dataSource->prepareImport(m_dataContainer, mode, actualRows, actualCols, varNames, columnModes);
0522 
0523     error = parse(fileName);
0524     if (error != READSTAT_OK) {
0525         DEBUG(Q_FUNC_INFO << ", ERROR parsing file " << STDSTRING(fileName))
0526         return;
0527     }
0528 
0529     DEBUG(Q_FUNC_INFO << ", column offset = " << columnOffset << " start/end column = " << startColumn << " / " << actualEndColumn)
0530     dataSource->finalizeImport(columnOffset, startColumn, actualEndColumn, QString(), mode);
0531 
0532     // value labels
0533     for (const auto& label : m_valueLabels)
0534         if (label.size() > 0)
0535             QDEBUG(Q_FUNC_INFO << ", label " << label << ", label values = " << m_labelSets[label].labels())
0536 
0537     QVector<Column*> columnList = dataSource->children<Column>();
0538     for (int i = 0; i < columnList.size(); i++) {
0539         auto* column = columnList.at(i);
0540         const QString label = m_valueLabels.at(i);
0541         if (column && label.size() > 0) {
0542             const auto columnMode = columnModes.at(i);
0543             const auto valueLabels = m_labelSets[label].labels();
0544             switch (columnMode) {
0545             case AbstractColumn::ColumnMode::Text:
0546                 for (int j = 0; j < valueLabels.size(); j++) {
0547                     DEBUG(Q_FUNC_INFO << ", column " << i << ": add string value label: " << STDSTRING(m_labelSets[label].valueString(j)) << " = "
0548                                       << STDSTRING(valueLabels.at(j)))
0549                     column->addValueLabel(m_labelSets[label].valueString(j), valueLabels.at(j));
0550                 }
0551                 break;
0552             case AbstractColumn::ColumnMode::Double:
0553                 for (int j = 0; j < valueLabels.size(); j++) {
0554                     DEBUG(Q_FUNC_INFO << ", column " << i << ": add double value label: " << m_labelSets[label].valueDouble(j) << " = "
0555                                       << STDSTRING(valueLabels.at(j)))
0556                     column->addValueLabel(m_labelSets[label].valueDouble(j), valueLabels.at(j));
0557                 }
0558                 break;
0559             case AbstractColumn::ColumnMode::Integer:
0560             case AbstractColumn::ColumnMode::BigInt:
0561                 for (int j = 0; j < valueLabels.size(); j++) {
0562                     DEBUG(Q_FUNC_INFO << ", column " << i << ": add integer value label: " << m_labelSets[label].valueInt(j) << " = "
0563                                       << STDSTRING(valueLabels.at(j)))
0564                     column->addValueLabel(m_labelSets[label].valueInt(j), valueLabels.at(j));
0565                 }
0566                 break;
0567             case AbstractColumn::ColumnMode::Month:
0568             case AbstractColumn::ColumnMode::Day:
0569             case AbstractColumn::ColumnMode::DateTime:
0570                 // not support by readstat
0571                 break;
0572             }
0573         }
0574     }
0575 
0576     dataSource->setComment(m_notes.join(QLatin1Char('\n')));
0577 #endif
0578 }
0579 
0580 /*!
0581     writes the content of \c dataSource to the file \c fileName.
0582 */
0583 void ReadStatFilterPrivate::write(const QString& /*fileName*/, AbstractDataSource* /*dataSource*/) {
0584     // TODO: writing ReadStat files not implemented yet
0585 }
0586 
0587 // ##############################################################################
0588 // ##################  Serialization/Deserialization  ###########################
0589 // ##############################################################################
0590 
0591 /*!
0592   Saves as XML.
0593  */
0594 void ReadStatFilter::save(QXmlStreamWriter* writer) const {
0595     writer->writeStartElement(QStringLiteral("readstatFilter"));
0596     writer->writeEndElement();
0597 }
0598 
0599 /*!
0600   Loads from XML.
0601 */
0602 bool ReadStatFilter::load(XmlStreamReader*) {
0603     return true;
0604 }