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