/*
* Copyright (C) 2009, 2010, 2011, 2012, 2013, 2017 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(),
matching_lines_(),
currentRegExp_(),
visibility_(),
filteredItemsCache_(),
workerThread_( nullptr ),
marks_()
{
/* 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(),
matching_lines_( SearchResultArray() ),
currentRegExp_(),
visibility_(),
filteredItemsCache_(),
workerThread_( logData ),
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 QRegularExpression& 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_ = QRegularExpression();
matching_lines_.clear();
maxLength_ = 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(
matching_lines_, lineNumber, &index);
}
int LogFilteredData::getLineIndexNumber( quint64 lineNumber ) const
{
int lineIndex = findFilteredLine( lineNumber );
return lineIndex;
}
LineNumber LogFilteredData::getNbTotalLines() const
{
return sourceLogData_->getNbLine();
}
LineNumber LogFilteredData::getNbMatches() const
{
return matching_lines_.size();
}
LineNumber 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 )
{
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
{
return marks_.getMark( mark );
}
bool LogFilteredData::isLineMarked( qint64 line ) const
{
return marks_.isLineMarked( line );
}
qint64 LogFilteredData::getMarkAfter( qint64 line ) const
{
qint64 marked_line = -1;
for ( auto i = marks_.begin(); i != marks_.end(); ++i ) {
if ( i->lineNumber() > line ) {
marked_line = i->lineNumber();
break;
}
}
return marked_line;
}
qint64 LogFilteredData::getMarkBefore( qint64 line ) const
{
qint64 marked_line = -1;
for ( auto i = marks_.begin(); i != marks_.end(); ++i ) {
if ( i->lineNumber() >= line ) {
break;
}
marked_line = i->lineNumber();
}
return marked_line;
}
void LogFilteredData::deleteMark( QChar mark )
{
marks_.deleteMark( mark );
filteredItemsCacheDirty_ = true;
// FIXME: maxLengthMarks_
}
void LogFilteredData::deleteMark( qint64 line )
{
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()
{
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_, &matching_lines_, &nbLinesProcessed_ );
filteredItemsCacheDirty_ = true;
emit searchProgressed( nbMatches, progress );
}
LineNumber LogFilteredData::findLogDataLine( LineNumber lineNum ) const
{
LineNumber line = std::numeric_limits::max();
if ( visibility_ == MatchesOnly ) {
if ( lineNum < matching_lines_.size() ) {
line = matching_lines_[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;
}
LineNumber LogFilteredData::findFilteredLine( LineNumber lineNum ) const
{
LineNumber lineIndex = std::numeric_limits::max();
if ( visibility_ == MatchesOnly ) {
lineIndex = lookupLineNumber( matching_lines_.begin(),
matching_lines_.end(),
lineNum );
}
else if ( visibility_ == MarksOnly ) {
lineIndex = lookupLineNumber( marks_.begin(),
marks_.end(),
lineNum );
}
else {
// Regenerate the cache if needed
if ( filteredItemsCacheDirty_ ) {
regenerateFilteredItemsCache();
}
lineIndex = lookupLineNumber( filteredItemsCache_.begin(),
filteredItemsCache_.end(),
lineNum );
}
return lineIndex;
}
// 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 = matching_lines_.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();
}
void LogFilteredData::doSetDisplayEncoding( Encoding encoding )
{
LOG(logDEBUG) << "AbstractLogData::setDisplayEncoding: " << static_cast( encoding );
}
void LogFilteredData::doSetMultibyteEncodingOffsets( int, int )
{
}
// 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( matching_lines_.size() + marks_.size() );
// (it's an overestimate but probably not by much so it's fine)
auto i = matching_lines_.cbegin();
Marks::const_iterator j = marks_.begin();
while ( ( i != matching_lines_.cend() ) || ( j != marks_.end() ) ) {
qint64 next_mark =
( j != marks_.end() ) ? j->lineNumber() : std::numeric_limits::max();
qint64 next_match =
( i != matching_lines_.cend() ) ? 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_.push_back( FilteredItem( next_mark, Mark ) );
if ( j != marks_.end() )
++j;
if ( ( next_mark == next_match ) && ( i != matching_lines_.cend() ) )
++i; // Case when it's both match and mark.
}
else {
// LOG(logDEBUG) << "Add match at " << next_match;
filteredItemsCache_.push_back( FilteredItem( next_match, Match ) );
if ( i != matching_lines_.cend() )
++i;
}
}
filteredItemsCacheDirty_ = false;
LOG(logDEBUG) << "finished regenerateFilteredItemsCache";
}