File indexing completed on 2024-04-14 03:40:55

0001 /*
0002     SPDX-FileCopyrightText: 2007 James B. Bowlin <bowlin@mindspring.com>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #pragma once
0008 
0009 #include <QFile>
0010 #include <QObject>
0011 #include <QTextStream>
0012 
0013 class QString;
0014 
0015 /**
0016  * @class KSFileReader
0017  * I totally rewrote this because the earlier scheme of reading all the lines of
0018  * a file into a buffer before processing is actually extremely inefficient
0019  * because it makes it impossible to interleave file reading and data processing
0020  * which all modern computers can do.  It also force large data files to be
0021  * split up into many smaller files which made dealing with the data much more
0022  * difficult and made the code to read in the data needlessly complicated.  A
0023  * simple subclassing of QTextStream fixed all of the above problems (IMO).
0024  *
0025  * I had added periodic progress reports based on line number to several parts
0026  * of the code that read in large files.  So I combined that duplicated code
0027  * into one place which was naturally here.  The progress code causes almost
0028  * nothing extra to take place when reading a file that does not use it.  The
0029  * only thing extra is incrementing the line number.  For files that you want to
0030  * emit periodic progress reports, you call setProgress() once setting up the
0031  * message to display and the intervals the message will be emitted.  Then
0032  * inside the loop you call showProgress().  This is an extra call in the read
0033  * loop but it just does an integer compare and almost always returns.  We could
0034  * inline it to reduce the call overhead but I don't think it makes a bit of
0035  * difference considering all of the processing that takes place when we read in
0036  * a line from a data file.
0037  *
0038  * NOTE: We no longer close the file like the previous version did.  I've
0039  * changed the code where this was assumed.
0040  *
0041  * There are two ways to use this class.  One is pass in a QFile& in the
0042  * constructor which is included only for backward compatibility.  The preferred
0043  * way is to just instantiate KSFileReader with no parameters and then use the
0044  * open( QString fname ) method to let this class handle the file opening which
0045  * helps take unneeded complexity out of the calling classes.  I didn't make a
0046  * constructor with the filename in it because we need to be able to inform the
0047  * caller of an error opening the file, hence the bool open(filename) method.
0048  *
0049  *
0050  * -- James B. Bowlin
0051  */
0052 
0053 class KSFileReader : public QObject, public QTextStream
0054 {
0055     Q_OBJECT
0056 
0057   public:
0058     /**
0059      * @short this is the preferred constructor.  You can then use
0060      * the open() method to let this class open the file for you.
0061      */
0062     explicit KSFileReader(qint64 maxLen = 1024);
0063 
0064     /**
0065      * Constructor
0066      * @param file is a previously opened (for reading) file.
0067      * @param maxLen sets the maximum line length before wrapping.  Setting
0068      * this parameter should help efficiency.  The little max-length.pl
0069      * script will tell you the maximum line length of files.
0070      */
0071     explicit KSFileReader(QFile &file, qint64 maxLen = 1024);
0072 
0073     /**
0074      * @short opens the file fname from the QStandardPaths::AppLocalDataLocation directory and uses that
0075      * file for the QTextStream.
0076      *
0077      * @param fname the name of the file to open
0078      * @return returns true on success.  Prints an error message and returns
0079      * false on failure.
0080      */
0081     bool open(const QString &fname);
0082 
0083     /**
0084      * @short opens the file with full path fname and uses that
0085      * file for the QTextStream. open() locates QStandardPaths::AppLocalDataLocation behind the scenes,
0086      * so passing fname such that
0087      * QString fname = KSPaths::locate(QStandardPaths::AppLocalDataLocation, "file_name" );
0088      * is equivalent
0089      *
0090      * @param fname full path to directory + name of the file to open
0091      * @return returns true on success.  Prints an error message and returns
0092      * false on failure.
0093      */
0094     bool openFullPath(const QString &fname);
0095 
0096     /**
0097      * @return true if we are not yet at the end of the file.
0098      * (I added this to be compatible with existing code.)
0099      */
0100     bool hasMoreLines() const { return !QTextStream::atEnd(); }
0101 
0102     /**
0103      * @short increments the line number and returns the next line from the file as a QString.
0104      */
0105     inline QString readLine()
0106     {
0107         m_curLine++;
0108         return QTextStream::readLine(m_maxLen);
0109     }
0110 
0111     /** @short returns the current line number */
0112     int lineNumber() const { return m_curLine; }
0113 
0114     /**
0115      * @short Prepares this instance to emit progress reports on how much
0116      * of the file has been read (in percent).
0117      * @param label the label
0118      * @param lastLine the number of lines to be read
0119      * @param numUpdates the number of progress reports to send
0120      */
0121     void setProgress(QString label, unsigned int lastLine, unsigned int numUpdates = 10);
0122 
0123     /**
0124      * @short emits progress reports when required and updates bookkeeping
0125      * for when to send the next report.  This might seem slow at first
0126      * glance but almost all the time we are just doing an integer compare
0127      * and returning.  If you are worried about speed we can inline it.
0128      * It could also safely be included in the readLine() method since
0129      * m_targetLine is set to MAXUINT in the constructor.
0130      */
0131     void showProgress();
0132 
0133   signals:
0134     void progressText(const QString &message);
0135 
0136   private:
0137     QFile m_file;
0138     qint64 m_maxLen;
0139     unsigned int m_curLine { 0 };
0140 
0141     unsigned int m_totalLines { 0 };
0142     unsigned int m_targetLine { UINT_MAX };
0143     unsigned int m_targetIncrement { 0 };
0144     QString m_label;
0145 };