xref: /glogg/src/data/logfiltereddata.cpp (revision 721a24330d187e5b0ceceec407aa3aa6931ec04b)
1 /*
2  * Copyright (C) 2009, 2010, 2011, 2012, 2013 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 // This file implements LogFilteredData
21 // It stores a pointer to the LogData that created it,
22 // so should always be destroyed before the LogData.
23 
24 #include "log.h"
25 
26 #include <QString>
27 #include <cassert>
28 #include <limits>
29 
30 #include "utils.h"
31 #include "logdata.h"
32 #include "marks.h"
33 #include "logfiltereddata.h"
34 
35 // Creates an empty set. It must be possible to display it without error.
36 // FIXME
37 LogFilteredData::LogFilteredData() : AbstractLogData(),
38     matchingLineList( QList<MatchingLine>() ),
39     currentRegExp_(),
40     visibility_(),
41     filteredItemsCache_(),
42     workerThread_( nullptr ),
43     marks_( new Marks() )
44 {
45     matchingLineList = QList<MatchingLine>();
46     /* Prevent any more searching */
47     maxLength_ = 0;
48     maxLengthMarks_ = 0;
49     searchDone_ = true;
50     visibility_ = MarksAndMatches;
51 
52     filteredItemsCacheDirty_ = true;
53 }
54 
55 // Usual constructor: just copy the data, the search is started by runSearch()
56 LogFilteredData::LogFilteredData( const LogData* logData )
57     : AbstractLogData(),
58     matchingLineList( SearchResultArray() ),
59     currentRegExp_(),
60     visibility_(),
61     filteredItemsCache_(),
62     workerThread_( logData ),
63     marks_( new Marks() )
64 {
65     // Starts with an empty result list
66     maxLength_ = 0;
67     maxLengthMarks_ = 0;
68     nbLinesProcessed_ = 0;
69 
70     sourceLogData_ = logData;
71 
72     searchDone_ = false;
73 
74     visibility_ = MarksAndMatches;
75 
76     filteredItemsCacheDirty_ = true;
77 
78     // Forward the update signal
79     connect( &workerThread_, SIGNAL( searchProgressed( int, int ) ),
80             this, SLOT( handleSearchProgressed( int, int ) ) );
81 
82     // Starts the worker thread
83     workerThread_.start();
84 }
85 
86 LogFilteredData::~LogFilteredData()
87 {
88     // FIXME
89     // workerThread_.stop();
90 }
91 
92 //
93 // Public functions
94 //
95 
96 // Run the search and send newDataAvailable() signals.
97 void LogFilteredData::runSearch( const QRegExp& regExp )
98 {
99     LOG(logDEBUG) << "Entering runSearch";
100 
101     clearSearch();
102     currentRegExp_ = regExp;
103 
104     workerThread_.search( currentRegExp_ );
105 }
106 
107 void LogFilteredData::updateSearch()
108 {
109     LOG(logDEBUG) << "Entering updateSearch";
110 
111     workerThread_.updateSearch( currentRegExp_, nbLinesProcessed_ );
112 }
113 
114 void LogFilteredData::interruptSearch()
115 {
116     LOG(logDEBUG) << "Entering interruptSearch";
117 
118     workerThread_.interrupt();
119 }
120 
121 void LogFilteredData::clearSearch()
122 {
123     currentRegExp_ = QRegExp();
124     matchingLineList.clear();
125     maxLength_        = 0;
126     maxLengthMarks_   = 0;
127     nbLinesProcessed_ = 0;
128     filteredItemsCacheDirty_ = true;
129 }
130 
131 qint64 LogFilteredData::getMatchingLineNumber( int matchNum ) const
132 {
133     qint64 matchingLine = findLogDataLine( matchNum );
134 
135     return matchingLine;
136 }
137 
138 // Scan the list for the 'lineNumber' passed
139 bool LogFilteredData::isLineInMatchingList( qint64 lineNumber )
140 {
141     int index;                                    // Not used
142     return lookupLineNumber<SearchResultArray>(
143             matchingLineList, lineNumber, &index);
144 }
145 
146 
147 qint64 LogFilteredData::getNbTotalLines() const
148 {
149     return sourceLogData_->getNbLine();
150 }
151 
152 int LogFilteredData::getNbMatches() const
153 {
154     return matchingLineList.size();
155 }
156 
157 int LogFilteredData::getNbMarks() const
158 {
159     return marks_->size();
160 }
161 
162 LogFilteredData::FilteredLineType
163     LogFilteredData::filteredLineTypeByIndex( int index ) const
164 {
165     // If we are only showing one type, the line is there because
166     // it is of this type.
167     if ( visibility_ == MatchesOnly )
168         return Match;
169     else if ( visibility_ == MarksOnly )
170         return Mark;
171     else {
172         // If it is MarksAndMatches, we have to look.
173         // Regenerate the cache if needed
174         if ( filteredItemsCacheDirty_ )
175             regenerateFilteredItemsCache();
176 
177         return filteredItemsCache_[ index ].type();
178     }
179 }
180 
181 // Delegation to our Marks object
182 
183 void LogFilteredData::addMark( qint64 line, QChar mark )
184 {
185     assert( marks_ );
186 
187     if ( ( line >= 0 ) && ( line < sourceLogData_->getNbLine() ) ) {
188         marks_->addMark( line, mark );
189         maxLengthMarks_ = qMax( maxLengthMarks_,
190                 sourceLogData_->getLineLength( line ) );
191         filteredItemsCacheDirty_ = true;
192     }
193     else
194         LOG(logERROR) << "LogFilteredData::addMark\
195  trying to create a mark outside of the file.";
196 }
197 
198 qint64 LogFilteredData::getMark( QChar mark ) const
199 {
200     assert( marks_ );
201 
202     return marks_->getMark( mark );
203 }
204 
205 bool LogFilteredData::isLineMarked( qint64 line ) const
206 {
207     assert( marks_ );
208 
209     return marks_->isLineMarked( line );
210 }
211 
212 void LogFilteredData::deleteMark( QChar mark )
213 {
214     assert( marks_ );
215 
216     marks_->deleteMark( mark );
217     filteredItemsCacheDirty_ = true;
218 
219     // FIXME: maxLengthMarks_
220 }
221 
222 void LogFilteredData::deleteMark( qint64 line )
223 {
224     assert( marks_ );
225 
226     marks_->deleteMark( line );
227     filteredItemsCacheDirty_ = true;
228 
229     // Now update the max length if needed
230     if ( sourceLogData_->getLineLength( line ) >= maxLengthMarks_ ) {
231         LOG(logDEBUG) << "deleteMark recalculating longest mark";
232         maxLengthMarks_ = 0;
233         for ( Marks::const_iterator i = marks_->begin();
234                 i != marks_->end(); ++i ) {
235             LOG(logDEBUG) << "line " << i->lineNumber();
236             maxLengthMarks_ = qMax( maxLengthMarks_,
237                     sourceLogData_->getLineLength( i->lineNumber() ) );
238         }
239     }
240 }
241 
242 void LogFilteredData::clearMarks()
243 {
244     assert( marks_ );
245 
246     marks_->clear();
247     filteredItemsCacheDirty_ = true;
248     maxLengthMarks_ = 0;
249 }
250 
251 void LogFilteredData::setVisibility( Visibility visi )
252 {
253     visibility_ = visi;
254 }
255 
256 //
257 // Slots
258 //
259 void LogFilteredData::handleSearchProgressed( int nbMatches, int progress )
260 {
261     LOG(logDEBUG) << "LogFilteredData::handleSearchProgressed matches="
262         << nbMatches << " progress=" << progress;
263 
264     // searchDone_ = true;
265     workerThread_.getSearchResult( &maxLength_, &matchingLineList, &nbLinesProcessed_ );
266     filteredItemsCacheDirty_ = true;
267 
268     emit searchProgressed( nbMatches, progress );
269 }
270 
271 qint64 LogFilteredData::findLogDataLine( qint64 lineNum ) const
272 {
273     qint64 line = 0;
274     if ( visibility_ == MatchesOnly ) {
275         if ( lineNum < matchingLineList.size() ) {
276             line = matchingLineList[lineNum].lineNumber();
277         }
278         else {
279             LOG(logERROR) << "Index too big in LogFilteredData: " << lineNum;
280         }
281     }
282     else if ( visibility_ == MarksOnly ) {
283         if ( lineNum < marks_->size() )
284             line = marks_->getLineMarkedByIndex( lineNum );
285         else
286             LOG(logERROR) << "Index too big in LogFilteredData: " << lineNum;
287     }
288     else {
289         // Regenerate the cache if needed
290         if ( filteredItemsCacheDirty_ )
291             regenerateFilteredItemsCache();
292 
293         if ( lineNum < filteredItemsCache_.size() )
294             line = filteredItemsCache_[ lineNum ].lineNumber();
295         else
296             LOG(logERROR) << "Index too big in LogFilteredData: " << lineNum;
297     }
298 
299     return line;
300 }
301 
302 // Implementation of the virtual function.
303 QString LogFilteredData::doGetLineString( qint64 lineNum ) const
304 {
305     qint64 line = findLogDataLine( lineNum );
306 
307     QString string = sourceLogData_->getLineString( line );
308     return string;
309 }
310 
311 // Implementation of the virtual function.
312 QString LogFilteredData::doGetExpandedLineString( qint64 lineNum ) const
313 {
314     qint64 line = findLogDataLine( lineNum );
315 
316     QString string = sourceLogData_->getExpandedLineString( line );
317     return string;
318 }
319 
320 // Implementation of the virtual function.
321 QStringList LogFilteredData::doGetLines( qint64 first_line, int number ) const
322 {
323     QStringList list;
324 
325     for ( int i = first_line; i < first_line + number; i++ ) {
326         list.append( doGetLineString( i ) );
327     }
328 
329     return list;
330 }
331 
332 // Implementation of the virtual function.
333 QStringList LogFilteredData::doGetExpandedLines( qint64 first_line, int number ) const
334 {
335     QStringList list;
336 
337     for ( int i = first_line; i < first_line + number; i++ ) {
338         list.append( doGetExpandedLineString( i ) );
339     }
340 
341     return list;
342 }
343 
344 // Implementation of the virtual function.
345 qint64 LogFilteredData::doGetNbLine() const
346 {
347     qint64 nbLines;
348 
349     if ( visibility_ == MatchesOnly )
350         nbLines = matchingLineList.size();
351     else if ( visibility_ == MarksOnly )
352         nbLines = marks_->size();
353     else {
354         // Regenerate the cache if needed (hopefully most of the time
355         // it won't be necessarily)
356         if ( filteredItemsCacheDirty_ )
357             regenerateFilteredItemsCache();
358         nbLines = filteredItemsCache_.size();
359     }
360 
361     return nbLines;
362 }
363 
364 // Implementation of the virtual function.
365 int LogFilteredData::doGetMaxLength() const
366 {
367     int max_length;
368 
369     if ( visibility_ == MatchesOnly )
370         max_length = maxLength_;
371     else if ( visibility_ == MarksOnly )
372         max_length = maxLengthMarks_;
373     else
374         max_length = qMax( maxLength_, maxLengthMarks_ );
375 
376     return max_length;
377 }
378 
379 // Implementation of the virtual function.
380 int LogFilteredData::doGetLineLength( qint64 lineNum ) const
381 {
382     qint64 line = findLogDataLine( lineNum );
383     return sourceLogData_->getExpandedLineString( line ).length();
384 }
385 
386 // TODO: We might be a bit smarter and not regenerate the whole thing when
387 // e.g. stuff is added at the end of the search.
388 void LogFilteredData::regenerateFilteredItemsCache() const
389 {
390     LOG(logDEBUG) << "regenerateFilteredItemsCache";
391 
392     filteredItemsCache_.clear();
393     filteredItemsCache_.reserve( matchingLineList.size() + marks_->size() );
394     // (it's an overestimate but probably not by much so it's fine)
395 
396     QList<MatchingLine>::const_iterator i = matchingLineList.begin();
397     Marks::const_iterator j = marks_->begin();
398 
399     while ( ( i != matchingLineList.end() ) || ( j != marks_->end() ) ) {
400         qint64 next_mark =
401             ( j != marks_->end() ) ? j->lineNumber() : std::numeric_limits<qint64>::max();
402         qint64 next_match =
403             ( i != matchingLineList.end() ) ? i->lineNumber() : std::numeric_limits<qint64>::max();
404         // We choose a Mark over a Match if a line is both, just an arbitrary choice really.
405         if ( next_mark <= next_match ) {
406             // LOG(logDEBUG) << "Add mark at " << next_mark;
407             filteredItemsCache_.append( FilteredItem( next_mark, Mark ) );
408             if ( j != marks_->end() )
409                 ++j;
410             if ( ( next_mark == next_match ) && ( i != matchingLineList.end() ) )
411                 ++i;  // Case when it's both match and mark.
412         }
413         else {
414             // LOG(logDEBUG) << "Add match at " << next_match;
415             filteredItemsCache_.append( FilteredItem( next_match, Match ) );
416             if ( i != matchingLineList.end() )
417                 ++i;
418         }
419     }
420 
421     filteredItemsCacheDirty_ = false;
422 
423     LOG(logDEBUG) << "finished regenerateFilteredItemsCache";
424 }
425