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 }