1 /*
2  * Copyright (C) 2009, 2010, 2013, 2014, 2015 Nicolas Bonnefon and other contributors
3  *
4  * This file is part of glogg.
5  *
6  * glogg is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * glogg is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with glogg.  If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #ifndef LOGDATA_H
21 #define LOGDATA_H
22 
23 #include <memory>
24 
25 #include <QObject>
26 #include <QString>
27 #include <QFile>
28 #include <QVector>
29 #include <QMutex>
30 #include <QDateTime>
31 #include <QTextCodec>
32 
33 #include "utils.h"
34 
35 #include "abstractlogdata.h"
36 #include "logdataworkerthread.h"
37 #include "filewatcher.h"
38 #include "loadingstatus.h"
39 
40 class LogFilteredData;
41 
42 // Thrown when trying to attach an already attached LogData
43 class CantReattachErr {};
44 
45 // Represents a complete set of data to be displayed (ie. a log file content)
46 // This class is thread-safe.
47 class LogData : public AbstractLogData {
48   Q_OBJECT
49 
50   public:
51     // Creates an empty LogData
52     LogData();
53     // Destroy an object
54     ~LogData();
55 
56     enum MonitoredFileStatus { Unchanged, DataAdded, Truncated };
57 
58     // Attaches the LogData to a file on disk
59     // It starts the asynchronous indexing and returns (almost) immediately
60     // Attaching to a non existant file works and the file is reported
61     // to be empty.
62     // Reattaching is forbidden and will throw.
63     void attachFile( const QString& fileName );
64     // Interrupt the loading and report a null file.
65     // Does nothing if no loading in progress.
66     void interruptLoading();
67     // Creates a new filtered data.
68     // ownership is passed to the caller
69     LogFilteredData* getNewFilteredData() const;
70     // Returns the size if the file in bytes
71     qint64 getFileSize() const;
72     // Returns the last modification date for the file.
73     // Null if the file is not on disk.
74     QDateTime getLastModifiedDate() const;
75     // Throw away all the file data and reload/reindex.
76     void reload();
77 
78     // Update the polling interval (in ms, 0 means disabled)
79     void setPollingInterval( uint32_t interval_ms );
80 
81     // Get the auto-detected encoding for the indexed text.
82     EncodingSpeculator::Encoding getDetectedEncoding() const;
83 
84   signals:
85     // Sent during the 'attach' process to signal progress
86     // percent being the percentage of completion.
87     void loadingProgressed( int percent );
88     // Signal the client the file is fully loaded and available.
89     void loadingFinished( LoadingStatus status );
90     // Sent when the file on disk has changed, will be followed
91     // by loadingProgressed if needed and then a loadingFinished.
92     void fileChanged( LogData::MonitoredFileStatus status );
93 
94   private slots:
95     // Consider reloading the file when it changes on disk updated
96     void fileChangedOnDisk();
97     // Called when the worker thread signals the current operation ended
98     void indexingFinished( LoadingStatus status );
99 
100   private:
101     // This class models an indexing operation.
102     // It exists to permit LogData to delay the operation if another
103     // one is ongoing (operations are asynchronous)
104     class LogDataOperation {
105       public:
LogDataOperation(const QString & fileName)106         LogDataOperation( const QString& fileName ) : filename_( fileName ) {}
107         // Permit each child to have its destructor
~LogDataOperation()108         virtual ~LogDataOperation() {};
109 
start(LogDataWorkerThread & workerThread)110         void start( LogDataWorkerThread& workerThread ) const
111         { doStart( workerThread ); }
getFilename()112         const QString& getFilename() const { return filename_; }
113 
114       protected:
115         virtual void doStart( LogDataWorkerThread& workerThread ) const = 0;
116         QString filename_;
117     };
118 
119     // Attaching a new file (change name + full index)
120     class AttachOperation : public LogDataOperation {
121       public:
AttachOperation(const QString & fileName)122         AttachOperation( const QString& fileName )
123             : LogDataOperation( fileName ) {}
~AttachOperation()124         ~AttachOperation() {};
125 
126       protected:
127         void doStart( LogDataWorkerThread& workerThread ) const;
128     };
129 
130     // Reindexing the current file
131     class FullIndexOperation : public LogDataOperation {
132       public:
FullIndexOperation()133         FullIndexOperation() : LogDataOperation( QString() ) {}
~FullIndexOperation()134         ~FullIndexOperation() {};
135 
136       protected:
137         void doStart( LogDataWorkerThread& workerThread ) const;
138     };
139 
140     // Indexing part of the current file (from fileSize)
141     class PartialIndexOperation : public LogDataOperation {
142       public:
PartialIndexOperation()143         PartialIndexOperation() : LogDataOperation( QString() ) {}
~PartialIndexOperation()144         ~PartialIndexOperation() {};
145 
146       protected:
147         void doStart( LogDataWorkerThread& workerThread ) const;
148     };
149 
150     std::shared_ptr<FileWatcher> fileWatcher_;
151     MonitoredFileStatus fileChangedOnDisk_;
152 
153     // Implementation of virtual functions
154     QString doGetLineString( qint64 line ) const override;
155     QString doGetExpandedLineString( qint64 line ) const override;
156     QStringList doGetLines( qint64 first, int number ) const override;
157     QStringList doGetExpandedLines( qint64 first, int number ) const override;
158     qint64 doGetNbLine() const override;
159     int doGetMaxLength() const override;
160     int doGetLineLength( qint64 line ) const override;
161     void doSetDisplayEncoding( const char* encoding ) override;
162 
163     void enqueueOperation( std::shared_ptr<const LogDataOperation> newOperation );
164     void startOperation();
165 
166     QString indexingFileName_;
167     std::unique_ptr<QFile> attached_file_;
168 
169     // Indexing data, read by us, written by the worker thread
170     IndexingData indexing_data_;
171 
172     QDateTime lastModifiedDate_;
173     std::shared_ptr<const LogDataOperation> currentOperation_;
174     std::shared_ptr<const LogDataOperation> nextOperation_;
175 
176     // Codec to decode text
177     QTextCodec* codec_;
178 
179     // To protect the file:
180     mutable QMutex fileMutex_;
181     // (are mutable to allow 'const' function to touch it,
182     // while remaining const)
183     // When acquiring both, data should be help before locking file.
184 
185     LogDataWorkerThread workerThread_;
186 };
187 
188 Q_DECLARE_METATYPE( LogData::MonitoredFileStatus );
189 
190 #endif
191