File indexing completed on 2024-04-21 03:42:13

0001 /*
0002     SPDX-FileCopyrightText: 2012 Rishab Arora <ra.rishab@gmail.com>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #pragma once
0008 
0009 #include <QHash>
0010 #include <QList>
0011 #include <QVariant>
0012 
0013 #include "ksfilereader.h"
0014 
0015 /**
0016  * @brief Generic class for text file parsers used in KStars.
0017  * Read rows using ReadCSVRow() regardless of the type of parser.
0018  * Usage:
0019  * 1) initialize KSParser
0020  * 2) while (KSParserObject.HasNextRow()) {
0021  *      QHash < Qstring, QVariant > read_stuff = KSParserObject.ReadNextRow();
0022  *      ...
0023  *      do what you need to do...
0024  *      ...
0025  *    }
0026  *
0027  * Debugging Information:
0028  * In case of read errors, the parsers emit a warning.
0029  * In case of conversion errors, the warnings are toggled by setting
0030  * const bool KSParser::parser_debug_mode_ = true; in ksparser.cpp
0031  *
0032  * In case of failure, the parser returns a Dummy Row. So if you see the
0033  * string "Null" in the returned QHash, it signifies the parserencountered an
0034  * unexpected error.
0035  **/
0036 class KSParser
0037 {
0038   public:
0039     /**
0040      * These are the values used in case of error in conversion
0041      **/
0042     static const double EBROKEN_DOUBLE;
0043     static const float EBROKEN_FLOAT;
0044     static const int EBROKEN_INT;
0045     static const QString EBROKEN_QSTRING;
0046 
0047     /**
0048      * @brief DataTypes for building sequence
0049      * D_QSTRING QString Type
0050      * D_INT Integer  Type
0051      * D_FLOAT Floating Point Type
0052      * D_DOUBLE Double PRecision Type
0053      * D_SKIP Unused Field. This string is not converted from QString
0054      **/
0055     enum DataTypes
0056     {
0057         D_QSTRING,
0058         D_INT,
0059         D_FLOAT,
0060         D_DOUBLE,
0061         D_SKIP
0062     };
0063 
0064     /**
0065      * @brief Returns a CSV parsing instance of a KSParser type object.
0066      *
0067      * Behavior:
0068      * 1) In case of attempt to read a non existent file, will return
0069      *    dummy row (not empty)
0070      * 2) In case of incomplete row, the whole row is ignored
0071      * 3) In case of missing values, parser will return empty string,
0072      *   or 0 or 0.0
0073      * 4) If you keep reading ignoring the HasNextRow you get dummy rows
0074      *
0075      * @param filename Full Path (Dir + Filename) of source file
0076      * @param comment_char Character signifying a comment line
0077      * @param sequence QList of QPairs of the form "field name,data type"
0078      * @param delimiter separate on which character. default ','
0079      **/
0080     KSParser(const QString &filename, const char comment_char, const QList<QPair<QString, DataTypes>> &sequence,
0081              const char delimiter = ',');
0082 
0083     /**
0084      * @brief Returns a Fixed Width parsing instance of a KSParser type object.
0085      *
0086      * Usage:
0087      * Important! The last value in width sequence is taken till the end of
0088      * line by default. This is done as the last line may or may not be padded
0089      * on the right.
0090      * Important! For backward compatibility, all string outputs are not
0091      * trimmed. Hence reading "hello  " will return "hello   " _not_ "hello"
0092      * If you need trimmed values like "hello" , use QString.trimmed()
0093      *
0094      * Behavior:
0095      * 1) In case of attempt to read a non existent file, will return
0096      *    dummy row (not empty!)
0097      * 2) In case of missing fields at the end, the line length is smaller
0098      *    than expected so it is skipped.
0099      * 3) If an integer or floating point value is empty (e.g. "    ")
0100      *    it is replaced by 0 or 0.0
0101      * 4) If you keep reading the file ignoring the HasNextRow(), you get
0102      *    Dummy Rows
0103      * @param filename Full Path (Dir + Filename) of source file
0104      * @param comment_char Character signifying a comment line
0105      * @param sequence QList of QPairs of the form "field name,data type"
0106      * @param widths width sequence. Last value is line.length() by default
0107      *               Hence, sequence.length() should be (width.length()+1)
0108      **/
0109     KSParser(const QString &filename, const char comment_char, const QList<QPair<QString, DataTypes>> &sequence,
0110              const QList<int> &widths);
0111 
0112     /**
0113      * @brief Generic function used to read the next row of a text file.
0114      * The constructor changes the function pointer to the appropriate function.
0115      * Returns the row as <"column name", value>
0116      *
0117      * @return QHash< QString, QVariant >
0118      **/
0119     QHash<QString, QVariant> ReadNextRow();
0120 
0121     /**
0122      * @brief Returns True if there are more rows to be read
0123      *
0124      * @return bool
0125      **/
0126     bool HasNextRow();
0127     // Too many warnings when const: datahandlers/ksparser.h:131:27: warning:
0128     // type qualifiers ignored on function return type [-Wignored-qualifiers]
0129 
0130     /**
0131      * @brief Wrapper function for KSFileReader setProgress
0132      *
0133      * @param msg What message to display
0134      * @param total_lines Total number of lines in file
0135      * @param step_size Size of step in emitting progress
0136      * @return void
0137      **/
0138     void SetProgress(QString msg, int total_lines, int step_size);
0139 
0140     /**
0141      * @brief Wrapper function for KSFileReader showProgress
0142      *
0143      * @return void
0144      **/
0145     void ShowProgress();
0146 
0147   private:
0148     /**
0149      * @brief Function Pointer used by ReadNextRow
0150      * to call the appropriate function among ReadCSVRow and ReadFixedWidthRow
0151      *
0152      * @return QHash< QString, QVariant >
0153      **/
0154     QHash<QString, QVariant> (KSParser::*readFunctionPtr)();
0155 
0156     /**
0157      * @brief Returns a single row from CSV.
0158      * If HasNextRow is false, returns a row with default values.
0159      *
0160      * @return QHash< QString, QVariant >
0161      **/
0162     QHash<QString, QVariant> ReadCSVRow();
0163 
0164     /**
0165      * @brief Returns a single row from Fixed Width File.
0166      * If HasNextRow is false, returns a row with default values.
0167      *
0168      * @return QHash< QString, QVariant >
0169      **/
0170     QHash<QString, QVariant> ReadFixedWidthRow();
0171 
0172     /**
0173      * @brief Returns a default value row.
0174      * Values are according to the current assigned sequence.
0175      *
0176      * @return QHash< QString, QVariant >
0177      **/
0178     QHash<QString, QVariant> DummyRow();
0179 
0180     /**
0181      * @brief This function combines the separated QString in case of quotes
0182      * The function can not handle stray quote marks.
0183      * eg. hello,"",world is acceptable
0184      *     hello"",world is not
0185      *
0186      * @param separated a list of QStrings separated at every delimiter
0187      * @return QList< QString >
0188      **/
0189     QList<QString> CombineQuoteParts(QList<QString> &separated);
0190 
0191     /**
0192      * @brief Function to return a QVariant of selected data type
0193      *
0194      * @param input_string QString of what the object should contain
0195      * @param data_type Data Type of input_string
0196      * @return QVariant
0197      **/
0198     QVariant ConvertToQVariant(const QString &input_string, const DataTypes &data_type, bool &ok);
0199 
0200     static const bool parser_debug_mode_;
0201 
0202     KSFileReader file_reader_;
0203     QString filename_;
0204     char comment_char_;
0205 
0206     QList<QPair<QString, DataTypes>> name_type_sequence_;
0207     QList<int> width_sequence_;
0208     char delimiter_ { 0 };
0209 };