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