xref: /glogg/src/data/logfiltereddata.cpp (revision ef13a4932c64b674c6bc7670b96c9b8f7298e666)
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     // Reset the search
102     currentRegExp_ = regExp;
103     matchingLineList.clear();
104     maxLength_ = 0;
105     maxLengthMarks_ = 0;
106 
107     workerThread_.search( currentRegExp_ );
108 }
109 
110 void LogFilteredData::updateSearch()
111 {
112     LOG(logDEBUG) << "Entering updateSearch";
113 
114     workerThread_.updateSearch( currentRegExp_, nbLinesProcessed_ );
115 }
116 
117 void LogFilteredData::interruptSearch()
118 {
119     LOG(logDEBUG) << "Entering interruptSearch";
120 
121     workerThread_.interrupt();
122 }
123 
124 void LogFilteredData::clearSearch()
125 {
126     currentRegExp_ = QRegExp();
127     matchingLineList.clear();
128     maxLength_ = 0;
129     filteredItemsCacheDirty_ = true;
130 }
131 
132 qint64 LogFilteredData::getMatchingLineNumber( int matchNum ) const
133 {
134     qint64 matchingLine = findLogDataLine( matchNum );
135 
136     return matchingLine;
137 }
138 
139 // Scan the list for the 'lineNumber' passed
140 bool LogFilteredData::isLineInMatchingList( qint64 lineNumber )
141 {
142     int index;                                    // Not used
143     return lookupLineNumber<SearchResultArray>(
144             matchingLineList, lineNumber, &index);
145 }
146 
147 
148 qint64 LogFilteredData::getNbTotalLines() const
149 {
150     return sourceLogData_->getNbLine();
151 }
152 
153 int LogFilteredData::getNbMatches() const
154 {
155     return matchingLineList.size();
156 }
157 
158 int LogFilteredData::getNbMarks() const
159 {
160     return marks_->size();
161 }
162 
163 LogFilteredData::FilteredLineType
164     LogFilteredData::filteredLineTypeByIndex( int index ) const
165 {
166     // If we are only showing one type, the line is there because
167     // it is of this type.
168     if ( visibility_ == MatchesOnly )
169         return Match;
170     else if ( visibility_ == MarksOnly )
171         return Mark;
172     else {
173         // If it is MarksAndMatches, we have to look.
174         // Regenerate the cache if needed
175         if ( filteredItemsCacheDirty_ )
176             regenerateFilteredItemsCache();
177 
178         return filteredItemsCache_[ index ].type();
179     }
180 }
181 
182 // Delegation to our Marks object
183 
184 void LogFilteredData::addMark( qint64 line, QChar mark )
185 {
186     assert( marks_ );
187 
188     if ( ( line >= 0 ) && ( line < sourceLogData_->getNbLine() ) ) {
189         marks_->addMark( line, mark );
190         maxLengthMarks_ = qMax( maxLengthMarks_,
191                 sourceLogData_->getLineLength( line ) );
192         filteredItemsCacheDirty_ = true;
193     }
194     else
195         LOG(logERROR) << "LogFilteredData::addMark\
196  trying to create a mark outside of the file.";
197 }
198 
199 qint64 LogFilteredData::getMark( QChar mark ) const
200 {
201     assert( marks_ );
202 
203     return marks_->getMark( mark );
204 }
205 
206 bool LogFilteredData::isLineMarked( qint64 line ) const
207 {
208     assert( marks_ );
209 
210     return marks_->isLineMarked( line );
211 }
212 
213 void LogFilteredData::deleteMark( QChar mark )
214 {
215     assert( marks_ );
216 
217     marks_->deleteMark( mark );
218     filteredItemsCacheDirty_ = true;
219 
220     // FIXME: maxLengthMarks_
221 }
222 
223 void LogFilteredData::deleteMark( qint64 line )
224 {
225     assert( marks_ );
226 
227     marks_->deleteMark( line );
228     filteredItemsCacheDirty_ = true;
229 
230     // Now update the max length if needed
231     if ( sourceLogData_->getLineLength( line ) >= maxLengthMarks_ ) {
232         LOG(logDEBUG) << "deleteMark recalculating longest mark";
233         maxLengthMarks_ = 0;
234         for ( Marks::const_iterator i = marks_->begin();
235                 i != marks_->end(); ++i ) {
236             LOG(logDEBUG) << "line " << i->lineNumber();
237             maxLengthMarks_ = qMax( maxLengthMarks_,
238                     sourceLogData_->getLineLength( i->lineNumber() ) );
239         }
240     }
241 }
242 
243 void LogFilteredData::clearMarks()
244 {
245     assert( marks_ );
246 
247     marks_->clear();
248     filteredItemsCacheDirty_ = true;
249     maxLengthMarks_ = 0;
250 }
251 
252 void LogFilteredData::setVisibility( Visibility visi )
253 {
254     visibility_ = visi;
255 }
256 
257 //
258 // Slots
259 //
260 void LogFilteredData::handleSearchProgressed( int nbMatches, int progress )
261 {
262     LOG(logDEBUG) << "LogFilteredData::handleSearchProgressed matches="
263         << nbMatches << " progress=" << progress;
264 
265     // searchDone_ = true;
266     workerThread_.getSearchResult( &maxLength_, &matchingLineList, &nbLinesProcessed_ );
267     filteredItemsCacheDirty_ = true;
268 
269     emit searchProgressed( nbMatches, progress );
270 }
271 
272 qint64 LogFilteredData::findLogDataLine( qint64 lineNum ) const
273 {
274     qint64 line = 0;
275     if ( visibility_ == MatchesOnly ) {
276         if ( lineNum < matchingLineList.size() ) {
277             line = matchingLineList[lineNum].lineNumber();
278         }
279         else {
280             LOG(logERROR) << "Index too big in LogFilteredData: " << lineNum;
281         }
282     }
283     else if ( visibility_ == MarksOnly ) {
284         if ( lineNum < marks_->size() )
285             line = marks_->getLineMarkedByIndex( lineNum );
286         else
287             LOG(logERROR) << "Index too big in LogFilteredData: " << lineNum;
288     }
289     else {
290         // Regenerate the cache if needed
291         if ( filteredItemsCacheDirty_ )
292             regenerateFilteredItemsCache();
293 
294         if ( lineNum < filteredItemsCache_.size() )
295             line = filteredItemsCache_[ lineNum ].lineNumber();
296         else
297             LOG(logERROR) << "Index too big in LogFilteredData: " << lineNum;
298     }
299 
300     return line;
301 }
302 
303 // Implementation of the virtual function.
304 QString LogFilteredData::doGetLineString( qint64 lineNum ) const
305 {
306     qint64 line = findLogDataLine( lineNum );
307 
308     QString string = sourceLogData_->getLineString( line );
309     return string;
310 }
311 
312 // Implementation of the virtual function.
313 QString LogFilteredData::doGetExpandedLineString( qint64 lineNum ) const
314 {
315     qint64 line = findLogDataLine( lineNum );
316 
317     QString string = sourceLogData_->getExpandedLineString( line );
318     return string;
319 }
320 
321 // Implementation of the virtual function.
322 QStringList LogFilteredData::doGetLines( qint64 first_line, int number ) const
323 {
324     QStringList list;
325 
326     for ( int i = first_line; i < first_line + number; i++ ) {
327         list.append( doGetLineString( i ) );
328     }
329 
330     return list;
331 }
332 
333 // Implementation of the virtual function.
334 QStringList LogFilteredData::doGetExpandedLines( qint64 first_line, int number ) const
335 {
336     QStringList list;
337 
338     for ( int i = first_line; i < first_line + number; i++ ) {
339         list.append( doGetExpandedLineString( i ) );
340     }
341 
342     return list;
343 }
344 
345 // Implementation of the virtual function.
346 qint64 LogFilteredData::doGetNbLine() const
347 {
348     qint64 nbLines;
349 
350     if ( visibility_ == MatchesOnly )
351         nbLines = matchingLineList.size();
352     else if ( visibility_ == MarksOnly )
353         nbLines = marks_->size();
354     else {
355         // Regenerate the cache if needed (hopefully most of the time
356         // it won't be necessarily)
357         if ( filteredItemsCacheDirty_ )
358             regenerateFilteredItemsCache();
359         nbLines = filteredItemsCache_.size();
360     }
361 
362     return nbLines;
363 }
364 
365 // Implementation of the virtual function.
366 int LogFilteredData::doGetMaxLength() const
367 {
368     int max_length;
369 
370     if ( visibility_ == MatchesOnly )
371         max_length = maxLength_;
372     else if ( visibility_ == MarksOnly )
373         max_length = maxLengthMarks_;
374     else
375         max_length = qMax( maxLength_, maxLengthMarks_ );
376 
377     return max_length;
378 }
379 
380 // Implementation of the virtual function.
381 int LogFilteredData::doGetLineLength( qint64 lineNum ) const
382 {
383     qint64 line = findLogDataLine( lineNum );
384     return sourceLogData_->getExpandedLineString( line ).length();
385 }
386 
387 // TODO: We might be a bit smarter and not regenerate the whole thing when
388 // e.g. stuff is added at the end of the search.
389 void LogFilteredData::regenerateFilteredItemsCache() const
390 {
391     LOG(logDEBUG) << "regenerateFilteredItemsCache";
392 
393     filteredItemsCache_.clear();
394     filteredItemsCache_.reserve( matchingLineList.size() + marks_->size() );
395     // (it's an overestimate but probably not by much so it's fine)
396 
397     QList<MatchingLine>::const_iterator i = matchingLineList.begin();
398     Marks::const_iterator j = marks_->begin();
399 
400     while ( ( i != matchingLineList.end() ) || ( j != marks_->end() ) ) {
401         qint64 next_mark =
402             ( j != marks_->end() ) ? j->lineNumber() : std::numeric_limits<qint64>::max();
403         qint64 next_match =
404             ( i != matchingLineList.end() ) ? i->lineNumber() : std::numeric_limits<qint64>::max();
405         // We choose a Mark over a Match if a line is both, just an arbitrary choice really.
406         if ( next_mark <= next_match ) {
407             // LOG(logDEBUG) << "Add mark at " << next_mark;
408             filteredItemsCache_.append( FilteredItem( next_mark, Mark ) );
409             if ( j != marks_->end() )
410                 ++j;
411             if ( ( next_mark == next_match ) && ( i != matchingLineList.end() ) )
412                 ++i;  // Case when it's both match and mark.
413         }
414         else {
415             // LOG(logDEBUG) << "Add match at " << next_match;
416             filteredItemsCache_.append( FilteredItem( next_match, Match ) );
417             if ( i != matchingLineList.end() )
418                 ++i;
419         }
420     }
421 
422     filteredItemsCacheDirty_ = false;
423 
424     LOG(logDEBUG) << "finished regenerateFilteredItemsCache";
425 }
426