/* * Copyright (C) 2009, 2010, 2011, 2012, 2013 Nicolas Bonnefon and other contributors * * This file is part of glogg. * * glogg is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * glogg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with glogg. If not, see . */ // This file implements LogFilteredData // It stores a pointer to the LogData that created it, // so should always be destroyed before the LogData. #include "log.h" #include #include #include #include "utils.h" #include "logdata.h" #include "marks.h" #include "logfiltereddata.h" // Creates an empty set. It must be possible to display it without error. // FIXME LogFilteredData::LogFilteredData() : AbstractLogData(), matchingLineList( QList() ), currentRegExp_(), visibility_(), filteredItemsCache_(), workerThread_( nullptr ), marks_( new Marks() ) { matchingLineList = QList(); /* Prevent any more searching */ maxLength_ = 0; maxLengthMarks_ = 0; searchDone_ = true; visibility_ = MarksAndMatches; filteredItemsCacheDirty_ = true; } // Usual constructor: just copy the data, the search is started by runSearch() LogFilteredData::LogFilteredData( const LogData* logData ) : AbstractLogData(), matchingLineList( SearchResultArray() ), currentRegExp_(), visibility_(), filteredItemsCache_(), workerThread_( logData ), marks_( new Marks() ) { // Starts with an empty result list maxLength_ = 0; maxLengthMarks_ = 0; nbLinesProcessed_ = 0; sourceLogData_ = logData; searchDone_ = false; visibility_ = MarksAndMatches; filteredItemsCacheDirty_ = true; // Forward the update signal connect( &workerThread_, SIGNAL( searchProgressed( int, int ) ), this, SLOT( handleSearchProgressed( int, int ) ) ); // Starts the worker thread workerThread_.start(); } LogFilteredData::~LogFilteredData() { // FIXME // workerThread_.stop(); } // // Public functions // // Run the search and send newDataAvailable() signals. void LogFilteredData::runSearch( const QRegExp& regExp ) { LOG(logDEBUG) << "Entering runSearch"; clearSearch(); currentRegExp_ = regExp; workerThread_.search( currentRegExp_ ); } void LogFilteredData::updateSearch() { LOG(logDEBUG) << "Entering updateSearch"; workerThread_.updateSearch( currentRegExp_, nbLinesProcessed_ ); } void LogFilteredData::interruptSearch() { LOG(logDEBUG) << "Entering interruptSearch"; workerThread_.interrupt(); } void LogFilteredData::clearSearch() { currentRegExp_ = QRegExp(); matchingLineList.clear(); maxLength_ = 0; maxLengthMarks_ = 0; nbLinesProcessed_ = 0; filteredItemsCacheDirty_ = true; } qint64 LogFilteredData::getMatchingLineNumber( int matchNum ) const { qint64 matchingLine = findLogDataLine( matchNum ); return matchingLine; } // Scan the list for the 'lineNumber' passed bool LogFilteredData::isLineInMatchingList( qint64 lineNumber ) { int index; // Not used return lookupLineNumber( matchingLineList, lineNumber, &index); } qint64 LogFilteredData::getNbTotalLines() const { return sourceLogData_->getNbLine(); } int LogFilteredData::getNbMatches() const { return matchingLineList.size(); } int LogFilteredData::getNbMarks() const { return marks_->size(); } LogFilteredData::FilteredLineType LogFilteredData::filteredLineTypeByIndex( int index ) const { // If we are only showing one type, the line is there because // it is of this type. if ( visibility_ == MatchesOnly ) return Match; else if ( visibility_ == MarksOnly ) return Mark; else { // If it is MarksAndMatches, we have to look. // Regenerate the cache if needed if ( filteredItemsCacheDirty_ ) regenerateFilteredItemsCache(); return filteredItemsCache_[ index ].type(); } } // Delegation to our Marks object void LogFilteredData::addMark( qint64 line, QChar mark ) { assert( marks_ ); if ( ( line >= 0 ) && ( line < sourceLogData_->getNbLine() ) ) { marks_->addMark( line, mark ); maxLengthMarks_ = qMax( maxLengthMarks_, sourceLogData_->getLineLength( line ) ); filteredItemsCacheDirty_ = true; } else LOG(logERROR) << "LogFilteredData::addMark\ trying to create a mark outside of the file."; } qint64 LogFilteredData::getMark( QChar mark ) const { assert( marks_ ); return marks_->getMark( mark ); } bool LogFilteredData::isLineMarked( qint64 line ) const { assert( marks_ ); return marks_->isLineMarked( line ); } void LogFilteredData::deleteMark( QChar mark ) { assert( marks_ ); marks_->deleteMark( mark ); filteredItemsCacheDirty_ = true; // FIXME: maxLengthMarks_ } void LogFilteredData::deleteMark( qint64 line ) { assert( marks_ ); marks_->deleteMark( line ); filteredItemsCacheDirty_ = true; // Now update the max length if needed if ( sourceLogData_->getLineLength( line ) >= maxLengthMarks_ ) { LOG(logDEBUG) << "deleteMark recalculating longest mark"; maxLengthMarks_ = 0; for ( Marks::const_iterator i = marks_->begin(); i != marks_->end(); ++i ) { LOG(logDEBUG) << "line " << i->lineNumber(); maxLengthMarks_ = qMax( maxLengthMarks_, sourceLogData_->getLineLength( i->lineNumber() ) ); } } } void LogFilteredData::clearMarks() { assert( marks_ ); marks_->clear(); filteredItemsCacheDirty_ = true; maxLengthMarks_ = 0; } void LogFilteredData::setVisibility( Visibility visi ) { visibility_ = visi; } // // Slots // void LogFilteredData::handleSearchProgressed( int nbMatches, int progress ) { LOG(logDEBUG) << "LogFilteredData::handleSearchProgressed matches=" << nbMatches << " progress=" << progress; // searchDone_ = true; workerThread_.getSearchResult( &maxLength_, &matchingLineList, &nbLinesProcessed_ ); filteredItemsCacheDirty_ = true; emit searchProgressed( nbMatches, progress ); } qint64 LogFilteredData::findLogDataLine( qint64 lineNum ) const { qint64 line = 0; if ( visibility_ == MatchesOnly ) { if ( lineNum < matchingLineList.size() ) { line = matchingLineList[lineNum].lineNumber(); } else { LOG(logERROR) << "Index too big in LogFilteredData: " << lineNum; } } else if ( visibility_ == MarksOnly ) { if ( lineNum < marks_->size() ) line = marks_->getLineMarkedByIndex( lineNum ); else LOG(logERROR) << "Index too big in LogFilteredData: " << lineNum; } else { // Regenerate the cache if needed if ( filteredItemsCacheDirty_ ) regenerateFilteredItemsCache(); if ( lineNum < filteredItemsCache_.size() ) line = filteredItemsCache_[ lineNum ].lineNumber(); else LOG(logERROR) << "Index too big in LogFilteredData: " << lineNum; } return line; } // Implementation of the virtual function. QString LogFilteredData::doGetLineString( qint64 lineNum ) const { qint64 line = findLogDataLine( lineNum ); QString string = sourceLogData_->getLineString( line ); return string; } // Implementation of the virtual function. QString LogFilteredData::doGetExpandedLineString( qint64 lineNum ) const { qint64 line = findLogDataLine( lineNum ); QString string = sourceLogData_->getExpandedLineString( line ); return string; } // Implementation of the virtual function. QStringList LogFilteredData::doGetLines( qint64 first_line, int number ) const { QStringList list; for ( int i = first_line; i < first_line + number; i++ ) { list.append( doGetLineString( i ) ); } return list; } // Implementation of the virtual function. QStringList LogFilteredData::doGetExpandedLines( qint64 first_line, int number ) const { QStringList list; for ( int i = first_line; i < first_line + number; i++ ) { list.append( doGetExpandedLineString( i ) ); } return list; } // Implementation of the virtual function. qint64 LogFilteredData::doGetNbLine() const { qint64 nbLines; if ( visibility_ == MatchesOnly ) nbLines = matchingLineList.size(); else if ( visibility_ == MarksOnly ) nbLines = marks_->size(); else { // Regenerate the cache if needed (hopefully most of the time // it won't be necessarily) if ( filteredItemsCacheDirty_ ) regenerateFilteredItemsCache(); nbLines = filteredItemsCache_.size(); } return nbLines; } // Implementation of the virtual function. int LogFilteredData::doGetMaxLength() const { int max_length; if ( visibility_ == MatchesOnly ) max_length = maxLength_; else if ( visibility_ == MarksOnly ) max_length = maxLengthMarks_; else max_length = qMax( maxLength_, maxLengthMarks_ ); return max_length; } // Implementation of the virtual function. int LogFilteredData::doGetLineLength( qint64 lineNum ) const { qint64 line = findLogDataLine( lineNum ); return sourceLogData_->getExpandedLineString( line ).length(); } // TODO: We might be a bit smarter and not regenerate the whole thing when // e.g. stuff is added at the end of the search. void LogFilteredData::regenerateFilteredItemsCache() const { LOG(logDEBUG) << "regenerateFilteredItemsCache"; filteredItemsCache_.clear(); filteredItemsCache_.reserve( matchingLineList.size() + marks_->size() ); // (it's an overestimate but probably not by much so it's fine) QList::const_iterator i = matchingLineList.begin(); Marks::const_iterator j = marks_->begin(); while ( ( i != matchingLineList.end() ) || ( j != marks_->end() ) ) { qint64 next_mark = ( j != marks_->end() ) ? j->lineNumber() : std::numeric_limits::max(); qint64 next_match = ( i != matchingLineList.end() ) ? i->lineNumber() : std::numeric_limits::max(); // We choose a Mark over a Match if a line is both, just an arbitrary choice really. if ( next_mark <= next_match ) { // LOG(logDEBUG) << "Add mark at " << next_mark; filteredItemsCache_.append( FilteredItem( next_mark, Mark ) ); if ( j != marks_->end() ) ++j; if ( ( next_mark == next_match ) && ( i != matchingLineList.end() ) ) ++i; // Case when it's both match and mark. } else { // LOG(logDEBUG) << "Add match at " << next_match; filteredItemsCache_.append( FilteredItem( next_match, Match ) ); if ( i != matchingLineList.end() ) ++i; } } filteredItemsCacheDirty_ = false; LOG(logDEBUG) << "finished regenerateFilteredItemsCache"; }