diff options
author | toma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2009-11-25 17:56:58 +0000 |
---|---|---|
committer | toma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2009-11-25 17:56:58 +0000 |
commit | bd9e6617827818fd043452c08c606f07b78014a0 (patch) | |
tree | 425bb4c3168f9c02f10150f235d2cb998dcc6108 /kompare/libdiff2 | |
download | tdesdk-bd9e6617827818fd043452c08c606f07b78014a0.tar.gz tdesdk-bd9e6617827818fd043452c08c606f07b78014a0.zip |
Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features.
BUG:215923
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdesdk@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'kompare/libdiff2')
26 files changed, 5411 insertions, 0 deletions
diff --git a/kompare/libdiff2/Makefile.am b/kompare/libdiff2/Makefile.am new file mode 100644 index 00000000..6f9048d8 --- /dev/null +++ b/kompare/libdiff2/Makefile.am @@ -0,0 +1,37 @@ +INCLUDES = \ + -I$(top_srcdir)/kompare/libdialogpages \ + -I$(top_srcdir)/kompare/komparepart \ + -I$(top_srcdir)/kompare/interfaces $(all_includes) + +noinst_HEADERS = \ + levenshteintable.h \ + kompare.h \ + kompareprocess.h \ + komparemodellist.h \ + diffmodel.h \ + difference.h \ + diffhunk.h + +# let automoc handle all of the meta source files (moc) +METASOURCES = AUTO + +noinst_LTLIBRARIES = libdiff2.la + +# the Part's source, library search path, and link libraries +libdiff2_la_SOURCES = \ + kompareprocess.cpp \ + komparemodellist.cpp \ + diffmodellist.cpp \ + diffmodel.cpp \ + difference.cpp \ + diffhunk.cpp \ + levenshteintable.cpp \ + parser.cpp \ + parserbase.cpp \ + cvsdiffparser.cpp \ + diffparser.cpp \ + perforceparser.cpp + +libdiff2_la_LDFLAGS = $(all_libraries) +libdiff2_la_LIBADD = $(LIB_KFILE) + diff --git a/kompare/libdiff2/cvsdiffparser.cpp b/kompare/libdiff2/cvsdiffparser.cpp new file mode 100644 index 00000000..d210eb66 --- /dev/null +++ b/kompare/libdiff2/cvsdiffparser.cpp @@ -0,0 +1,160 @@ +/************************************************************************** +** cvsdiffparser.cpp +** ----------------- +** begin : Sun Aug 4 15:05:35 2002 +** copyright : (C) 2002-2004 Otto Bruggeman +** email : otto.bruggeman@home.nl +** +***************************************************************************/ +/*************************************************************************** +** +** This program 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 2 of the License, or +** ( at your option ) any later version. +** +***************************************************************************/ + +#include <qregexp.h> + +#include <kdebug.h> + +#include "cvsdiffparser.h" +#include "komparemodellist.h" + + +using namespace Diff2; + +CVSDiffParser::CVSDiffParser( const KompareModelList* list, const QStringList& diff ) : ParserBase( list, diff ) +{ + // The regexps needed for context cvs diff parsing, the rest is the same as in parserbase.cpp + // third capture in header1 is non optional for cvs diff, it is the revision + m_contextDiffHeader1.setPattern( "\\*\\*\\* ([^\\t]+)\\t([^\\t]+)\\t(.*)\\n" ); + m_contextDiffHeader2.setPattern( "--- ([^\\t]+)\\t([^\\t]+)(|\\t(.*))\\n" ); + + m_normalDiffHeader.setPattern( "Index: (.*)\\n" ); +} + +CVSDiffParser::~CVSDiffParser() +{ +} + +enum Kompare::Format CVSDiffParser::determineFormat() +{ +// kdDebug(8101) << "Determining the format of the CVSDiff" << endl; + + QRegExp normalRE ( "[0-9]+[0-9,]*[acd][0-9]+[0-9,]*" ); + QRegExp unifiedRE( "^--- [^\\t]+\\t" ); + QRegExp contextRE( "^\\*\\*\\* [^\\t]+\\t" ); + QRegExp rcsRE ( "^[acd][0-9]+ [0-9]+" ); + QRegExp edRE ( "^[0-9]+[0-9,]*[acd]" ); + + QStringList::ConstIterator it = m_diffLines.begin(); + + while( it != m_diffLines.end() ) + { + if( (*it).find( normalRE, 0 ) == 0 ) + { +// kdDebug(8101) << "Difflines are from a Normal diff..." << endl; + return Kompare::Normal; + } + else if( (*it).find( unifiedRE, 0 ) == 0 ) + { +// kdDebug(8101) << "Difflines are from a Unified diff..." << endl; + return Kompare::Unified; + } + else if( (*it).find( contextRE, 0 ) == 0 ) + { +// kdDebug(8101) << "Difflines are from a Context diff..." << endl; + return Kompare::Context; + } + else if( (*it).find( rcsRE, 0 ) == 0 ) + { +// kdDebug(8101) << "Difflines are from a RCS diff..." << endl; + return Kompare::RCS; + } + else if( (*it).find( edRE, 0 ) == 0 ) + { +// kdDebug(8101) << "Difflines are from an ED diff..." << endl; + return Kompare::Ed; + } + ++it; + } +// kdDebug(8101) << "Difflines are from an unknown diff..." << endl; + return Kompare::UnknownFormat; +} + +bool CVSDiffParser::parseNormalDiffHeader() +{ + kdDebug(8101) << "CVSDiffParser::parseNormalDiffHeader()" << endl; + bool result = false; + + QStringList::ConstIterator diffEnd = m_diffLines.end(); + + while ( m_diffIterator != diffEnd ) + { + if ( m_normalDiffHeader.exactMatch( *m_diffIterator ) ) + { + kdDebug(8101) << "Matched length Header = " << m_normalDiffHeader.matchedLength() << endl; + kdDebug(8101) << "Matched string Header = " << m_normalDiffHeader.cap( 0 ) << endl; + + m_currentModel = new DiffModel(); + QObject::connect( m_currentModel, SIGNAL( setModified( bool ) ), m_list, SLOT( slotSetModified( bool ) ) ); + m_currentModel->setSourceFile ( m_normalDiffHeader.cap( 1 ) ); + m_currentModel->setDestinationFile ( m_normalDiffHeader.cap( 1 ) ); + + result = true; + + ++m_diffIterator; + break; + } + else + { + kdDebug(8101) << "No match for: " << ( *m_diffIterator ) << endl; + } + ++m_diffIterator; + } + + if ( result == false ) + { + // Set this to the first line again and hope it is a single file diff + m_diffIterator = m_diffLines.begin(); + m_currentModel = new DiffModel(); + QObject::connect( m_currentModel, SIGNAL( setModified( bool ) ), m_list, SLOT( slotSetModified( bool ) ) ); + m_singleFileDiff = true; + } + + return result; +} + + +bool CVSDiffParser::parseEdDiffHeader() +{ + return false; +} + +bool CVSDiffParser::parseRCSDiffHeader() +{ + return false; +} + +bool CVSDiffParser::parseEdHunkHeader() +{ + return false; +} + +bool CVSDiffParser::parseRCSHunkHeader() +{ + return false; +} + +bool CVSDiffParser::parseEdHunkBody() +{ + return false; +} + +bool CVSDiffParser::parseRCSHunkBody() +{ + return false; +} + diff --git a/kompare/libdiff2/cvsdiffparser.h b/kompare/libdiff2/cvsdiffparser.h new file mode 100644 index 00000000..88fef485 --- /dev/null +++ b/kompare/libdiff2/cvsdiffparser.h @@ -0,0 +1,61 @@ +/************************************************************************** +** cvsdiffparser.h +** ----------------- +** begin : Sun Aug 4 15:05:35 2002 +** copyright : (C) 2002-2004 Otto Bruggeman +** email : otto.bruggeman@home.nl +** +***************************************************************************/ +/*************************************************************************** +** +** This program 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 2 of the License, or +** ( at your option ) any later version. +** +***************************************************************************/ + +#ifndef _CVSDIFF_PARSER_H +#define _CVSDIFF_PARSER_H + +#include <qregexp.h> + +#include "parserbase.h" + +namespace Diff2 +{ + +class KompareModelList; + +class CVSDiffParser : public ParserBase +{ +public: + CVSDiffParser( const KompareModelList* list, const QStringList& diff ); + virtual ~CVSDiffParser(); + +protected: + virtual enum Kompare::Format determineFormat(); + +protected: +// virtual bool parseContextDiffHeader(); + virtual bool parseEdDiffHeader(); + virtual bool parseNormalDiffHeader(); + virtual bool parseRCSDiffHeader(); +// virtual bool parseUnifiedDiffHeader(); + +// virtual bool parseContextHunkHeader(); + virtual bool parseEdHunkHeader(); +// virtual bool parseNormalHunkHeader(); + virtual bool parseRCSHunkHeader(); +// virtual bool parseUnifiedHunkHeader(); + +// virtual bool parseContextHunkBody(); + virtual bool parseEdHunkBody(); +// virtual bool parseNormalHunkBody(); + virtual bool parseRCSHunkBody(); +// virtual bool parseUnifiedHunkBody(); +}; + +} // End of namespace Diff2 + +#endif diff --git a/kompare/libdiff2/difference.cpp b/kompare/libdiff2/difference.cpp new file mode 100644 index 00000000..8cbb4093 --- /dev/null +++ b/kompare/libdiff2/difference.cpp @@ -0,0 +1,137 @@ +/*************************************************************************** + difference.cpp - description + ------------------- + begin : Sun Mar 4 2001 + copyright : (C) 2001-2003 by Otto Bruggeman + and John Firebaugh + email : otto.bruggeman@home.nl + jfirebaugh@kde.org +****************************************************************************/ + +/*************************************************************************** +** +** This program 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 2 of the License, or +** (at your option) any later version. +** +***************************************************************************/ + +#include "difference.h" +#include "levenshteintable.h" + +using namespace Diff2; + +Difference::Difference( int sourceLineNo, int destinationLineNo, int type ) : + m_type( type ), + m_sourceLineNo( sourceLineNo ), + m_destinationLineNo( destinationLineNo ), + m_applied( false ) +{ +} + +Difference::~Difference() +{ +} + +void Difference::addSourceLine( QString line ) +{ + m_sourceLines.append( new DifferenceString( line ) ); +} + +void Difference::addDestinationLine( QString line ) +{ + m_destinationLines.append( new DifferenceString( line ) ); +} + +int Difference::sourceLineCount() const +{ + return m_sourceLines.count(); +} + +int Difference::destinationLineCount() const +{ + return m_destinationLines.count(); +} + +void Difference::apply( bool apply ) +{ + m_applied = apply; +} + +void Difference::determineInlineDifferences() +{ + LevenshteinTable table; + if ( m_type != Difference::Change ) + return; + + // Do nothing for now when the slc != dlc + // One could try to find the closest matching destination string for any + // of the source strings but this is compute intensive + if ( sourceLineCount() != destinationLineCount() ) + return; + + int slc = sourceLineCount(); + + for ( int i = 0; i < slc; ++i ) + { + DifferenceString* sl = sourceLineAt( i ); + DifferenceString* dl = destinationLineAt( i ); + + // FIXME: If the table cant be created dont do the rest + table.createTable( sl, dl ); + + table.createListsOfMarkers(); + } +} + +QString Difference::recreateDifference() const +{ + QString difference; + + // source + DifferenceStringListConstIterator stringIt = m_sourceLines.begin(); + DifferenceStringListConstIterator sEnd = m_sourceLines.end(); + + for ( ; stringIt != sEnd; ++stringIt ) + { + switch ( m_type ) + { + case Change: + case Delete: + difference += "-"; + break; + default: + // Insert but this is not possible in source + // Unchanged will be handled in destination + // since they are the same +// kdDebug( 8101 ) << "Go away, nothing to do for you in source..." << endl; + continue; + } + difference += (*stringIt)->string(); + } + + //destination + stringIt = m_destinationLines.begin(); + sEnd = m_destinationLines.end(); + + for ( ; stringIt != sEnd; ++stringIt ) + { + switch ( m_type ) + { + case Change: + case Insert: + difference += "+"; + break; + case Unchanged: + difference += " "; + break; + default: // Delete but this is not possible in destination +// kdDebug( 8101 ) << "Go away, nothing to do for you in destination..." << endl; + continue; + } + difference += (*stringIt)->string(); + } + + return difference; +} diff --git a/kompare/libdiff2/difference.h b/kompare/libdiff2/difference.h new file mode 100644 index 00000000..91065891 --- /dev/null +++ b/kompare/libdiff2/difference.h @@ -0,0 +1,223 @@ +/*************************************************************************** + difference.h - description + ------------------- + begin : Sun Mar 4 2001 + copyright : (C) 2001-2003 by Otto Bruggeman + and John Firebaugh + email : otto.bruggeman@home.nl + jfirebaugh@kde.org +****************************************************************************/ + +/*************************************************************************** +** +** This program 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 2 of the License, or +** (at your option) any later version. +** +***************************************************************************/ + +#ifndef DIFFERENCE_H +#define DIFFERENCE_H + +#include <qvaluelist.h> +#include <qvaluevector.h> + +#include <kdebug.h> + +class QString; + +namespace Diff2 +{ + +class LevenshteinTable; + +class Marker +{ +public: + enum Type { Start = 0, End = 1 }; + +public: + Marker() + { + m_type = Marker::Start; + m_offset = 0; + } + Marker( enum Marker::Type type, unsigned int offset ) + { + m_type = type; + m_offset = offset; + } + ~Marker() {} + +public: + enum Marker::Type type() const { return m_type; } + unsigned int offset() const { return m_offset; } + + void setType ( enum Marker::Type type ) { m_type = type; } + void setOffset( unsigned int offset ) { m_offset = offset; } + +private: + enum Marker::Type m_type; + unsigned int m_offset; +}; + +typedef QValueList<Marker*> MarkerList; +typedef QValueList<Marker*>::iterator MarkerListIterator; +typedef QValueList<Marker*>::const_iterator MarkerListConstIterator; + +class DifferenceString +{ +public: + DifferenceString() + { +// kdDebug(8101) << "DifferenceString::DifferenceString()" << endl; + } + DifferenceString( const QString& string, const MarkerList& markerList = MarkerList() ) : + m_string( string ), + m_markerList( markerList ) + { +// kdDebug(8101) << "DifferenceString::DifferenceString( " << string << ", " << markerList << " )" << endl; + calculateHash(); + } + DifferenceString( const DifferenceString& ds ) : + m_string( ds.m_string ), + m_conflict( ds.m_conflict ), + m_hash( ds.m_hash ), + m_markerList( ds.m_markerList ) + { +// kdDebug(8101) << "DifferenceString::DifferenceString( const DifferenceString& " << ds << " )" << endl; + } + ~DifferenceString() {} + +public: + const QString& string() const + { + return m_string; + } + const QString& conflictString() const + { + return m_conflict; + } + const MarkerList& markerList() + { + return m_markerList; + } + void setString( const QString& string ) + { + m_string = string; + calculateHash(); + } + void setConflictString( const QString& conflict ) + { + m_conflict = conflict; + } + void setMarkerList( const MarkerList& markerList ) + { + m_markerList = markerList; + } + void prepend( Marker* marker ) + { + m_markerList.prepend( marker ); + } + bool operator==( const DifferenceString& ks ) + { + if ( m_hash != ks.m_hash ) + return false; + return m_string == ks.m_string; + } + +protected: + void calculateHash() + { + unsigned short const* str = reinterpret_cast<unsigned short const*>( m_string.unicode() ); + const unsigned int len = m_string.length(); + + m_hash = 1315423911; + + for ( unsigned int i = 0; i < len; i++ ) + { + m_hash ^= ( m_hash << 5 ) + str[i] + ( m_hash >> 2 ); + } + } + +private: + QString m_string; + QString m_conflict; + unsigned int m_hash; + MarkerList m_markerList; +}; + +typedef QValueVector<DifferenceString*> DifferenceStringList; +typedef QValueVector<DifferenceString*>::iterator DifferenceStringListIterator; +typedef QValueVector<DifferenceString*>::const_iterator DifferenceStringListConstIterator; + +class Difference +{ +public: + enum Type { Change, Insert, Delete, Unchanged }; + +public: + Difference( int sourceLineNo, int destinationLineNo, int type = Difference::Unchanged ); + ~Difference(); + +public: + int type() const { return m_type; }; + + int sourceLineNumber() const { return m_sourceLineNo; } + int destinationLineNumber() const { return m_destinationLineNo; } + + int sourceLineCount() const; + int destinationLineCount() const; + + DifferenceString* sourceLineAt( int i ) { return m_sourceLines[ i ]; } + DifferenceString* destinationLineAt( int i ) { return m_destinationLines[ i ]; } + + const DifferenceStringList sourceLines() const { return m_sourceLines; } + const DifferenceStringList destinationLines() const { return m_destinationLines; } + + bool hasConflict() const + { + return m_conflicts; + } + void setConflict( bool conflicts ) + { + m_conflicts = conflicts; + } + + void apply( bool apply ); + bool applied() const { return m_applied; } + + void setType( int type ) { m_type = type; } + + void addSourceLine( QString line ); + void addDestinationLine( QString line ); + + /** This method will calculate the differences between the individual strings and store them as Markers */ + void determineInlineDifferences(); + + QString recreateDifference() const; + +private: + int m_type; + + int m_sourceLineNo; + int m_destinationLineNo; + + DifferenceStringList m_sourceLines; + DifferenceStringList m_destinationLines; + + bool m_applied; + bool m_conflicts; + + LevenshteinTable* m_tableXXX; // now unused +}; + +typedef QValueList<Difference*> DifferenceList; +typedef QValueList<Difference*>::iterator DifferenceListIterator; +typedef QValueList<Difference*>::const_iterator DifferenceListConstIterator; + +} // End of namespace Diff2 + +#endif + diff --git a/kompare/libdiff2/diffhunk.cpp b/kompare/libdiff2/diffhunk.cpp new file mode 100644 index 00000000..f980dd93 --- /dev/null +++ b/kompare/libdiff2/diffhunk.cpp @@ -0,0 +1,115 @@ +/*************************************************************************** + diffhunk.cpp - description + ------------------- + begin : Sun Mar 4 2001 + copyright : (C) 2001-2003 by Otto Bruggeman + and John Firebaugh + email : otto.bruggeman@home.nl + jfirebaugh@kde.org +****************************************************************************/ + +/*************************************************************************** +** +** This program 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 2 of the License, or +** (at your option) any later version. +** +***************************************************************************/ + + +#include "difference.h" +#include "diffhunk.h" + +using namespace Diff2; + +DiffHunk::DiffHunk( int sourceLine, int destinationLine, QString function, Type type ) : + m_sourceLine( sourceLine ), + m_destinationLine( destinationLine ), + m_function( function ), + m_type( type ) +{ +} + +DiffHunk::~DiffHunk() +{ +} + +void DiffHunk::add( Difference* diff ) +{ + m_differences.append( diff ); +} + +int DiffHunk::sourceLineCount() const +{ + DifferenceListConstIterator diffIt = m_differences.begin(); + DifferenceListConstIterator dEnd = m_differences.end(); + + int lineCount = 0; + + for ( ; diffIt != dEnd; ++diffIt ) + lineCount += (*diffIt)->sourceLineCount(); + + return lineCount; +} + +int DiffHunk::destinationLineCount() const +{ + DifferenceListConstIterator diffIt = m_differences.begin(); + DifferenceListConstIterator dEnd = m_differences.end(); + + int lineCount = 0; + + for ( ; diffIt != dEnd; ++diffIt ) + lineCount += (*diffIt)->destinationLineCount(); + + return lineCount; +} + +QString DiffHunk::recreateHunk() const +{ + QString hunk; + QString differences; + + // recreate body + DifferenceListConstIterator diffIt = m_differences.begin(); + DifferenceListConstIterator dEnd = m_differences.end(); + + int slc = 0; // source line count + int dlc = 0; // destination line count + for ( ; diffIt != dEnd; ++diffIt ) + { + switch ( (*diffIt)->type() ) + { + case Difference::Unchanged: + case Difference::Change: + slc += (*diffIt)->sourceLineCount(); + dlc += (*diffIt)->destinationLineCount(); + break; + case Difference::Insert: + dlc += (*diffIt)->destinationLineCount(); + break; + case Difference::Delete: + slc += (*diffIt)->sourceLineCount(); + break; + } + differences += (*diffIt)->recreateDifference(); + } + + // recreate header + hunk += QString::fromLatin1( "@@ -%1,%3 +%2,%4 @@" ) + .arg( m_sourceLine ) + .arg( m_destinationLine ) + .arg( slc ) + .arg( dlc ); + + if ( !m_function.isEmpty() ) + hunk += " " + m_function; + + hunk += QString::fromLatin1( "\n" ); + + hunk += differences; + + kdDebug( 8101 ) << hunk << endl; + return hunk; +} diff --git a/kompare/libdiff2/diffhunk.h b/kompare/libdiff2/diffhunk.h new file mode 100644 index 00000000..8a76babb --- /dev/null +++ b/kompare/libdiff2/diffhunk.h @@ -0,0 +1,69 @@ +/*************************************************************************** + diffhunk.h - description + ------------------- + begin : Sun Mar 4 2001 + copyright : (C) 2001-2003 by Otto Bruggeman + and John Firebaugh + email : otto.bruggeman@home.nl + jfirebaugh@kde.org +****************************************************************************/ + +/*************************************************************************** +** +** This program 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 2 of the License, or +** (at your option) any later version. +** +***************************************************************************/ + +#ifndef DIFFHUNK_H +#define DIFFHUNK_H + +#include "difference.h" + +namespace Diff2 +{ + +class Difference; + +class DiffHunk +{ +public: + enum Type { Normal, AddedByBlend }; + +public: + DiffHunk( int sourceLine, int destinationLine, QString function = QString::null, Type type = Normal ); + ~DiffHunk(); + + const DifferenceList& differences() const { return m_differences; }; + const QString& function() const { return m_function; }; + + int sourceLineNumber() const { return m_sourceLine; }; + int destinationLineNumber() const { return m_destinationLine; }; + + int sourceLineCount() const; + int destinationLineCount() const; + + const Type type() const { return m_type; } + void setType( Type type ) { m_type = type; } + + void add( Difference* diff ); + + QString recreateHunk() const; + +private: + int m_sourceLine; + int m_destinationLine; + DifferenceList m_differences; + QString m_function; + Type m_type; +}; + +typedef QValueList<DiffHunk*> DiffHunkList; +typedef QValueList<DiffHunk*>::iterator DiffHunkListIterator; +typedef QValueList<DiffHunk*>::const_iterator DiffHunkListConstIterator; + +} // End of namespace Diff2 + +#endif diff --git a/kompare/libdiff2/diffmodel.cpp b/kompare/libdiff2/diffmodel.cpp new file mode 100644 index 00000000..54c33457 --- /dev/null +++ b/kompare/libdiff2/diffmodel.cpp @@ -0,0 +1,409 @@ +/*************************************************************************** + diffmodel.cpp - description + ------------------- + begin : Sun Mar 4 2001 + copyright : (C) 2001-2004 Otto Bruggeman + (C) 2001-2003 John Firebaugh + email : otto.bruggeman@home.nl + jfirebaugh@kde.org +****************************************************************************/ + +/*************************************************************************** +** +** This program 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 2 of the License, or +** (at your option) any later version. +** +***************************************************************************/ + +#include <qregexp.h> +#include <qvaluelist.h> + +#include <kdebug.h> +#include <klocale.h> + +#include "difference.h" +#include "diffhunk.h" +#include "diffmodel.h" + +using namespace Diff2; + +/** */ +DiffModel::DiffModel( const QString& source, const QString& destination ) : + m_source( source ), + m_destination( destination ), + m_sourcePath( "" ), + m_destinationPath( "" ), + m_sourceFile( "" ), + m_destinationFile( "" ), + m_sourceTimestamp( "" ), + m_destinationTimestamp( "" ), + m_sourceRevision( "" ), + m_destinationRevision( "" ), + m_appliedCount( 0 ), + m_modified( false ), + m_diffIndex( 0 ), + m_selectedDifference( 0 ), + m_blended( false ) +{ + splitSourceInPathAndFileName(); + splitDestinationInPathAndFileName(); +} + +DiffModel::DiffModel() : + m_source( "" ), + m_destination( "" ), + m_sourcePath( "" ), + m_destinationPath( "" ), + m_sourceFile( "" ), + m_destinationFile( "" ), + m_sourceTimestamp( "" ), + m_destinationTimestamp( "" ), + m_sourceRevision( "" ), + m_destinationRevision( "" ), + m_appliedCount( 0 ), + m_modified( false ), + m_diffIndex( 0 ), + m_selectedDifference( 0 ), + m_blended( false ) +{ +} + +/** */ +DiffModel::~DiffModel() +{ +} + +void DiffModel::splitSourceInPathAndFileName() +{ + int pos; + + if( ( pos = m_source.findRev( "/" ) ) >= 0 ) + m_sourcePath = m_source.mid( 0, pos+1 ); + + if( ( pos = m_source.findRev( "/" ) ) >= 0 ) + m_sourceFile = m_source.mid( pos+1, m_source.length() - pos ); + else + m_sourceFile = m_source; + + kdDebug(8101) << m_source << " was split into " << m_sourcePath << " and " << m_sourceFile << endl; +} + +void DiffModel::splitDestinationInPathAndFileName() +{ + int pos; + + if( ( pos = m_destination.findRev( "/" ) )>= 0 ) + m_destinationPath = m_destination.mid( 0, pos+1 ); + + if( ( pos = m_destination.findRev( "/" ) ) >= 0 ) + m_destinationFile = m_destination.mid( pos+1, m_destination.length() - pos ); + else + m_destinationFile = m_source; + + kdDebug(8101) << m_destination << " was split into " << m_destinationPath << " and " << m_destinationFile << endl; +} + +DiffModel& DiffModel::operator=( const DiffModel& model ) +{ + if ( &model != this ) // Guard from self-assignment + { + m_source = model.m_source; + m_destination = model.m_destination; + m_sourcePath = model.m_sourcePath; + m_sourceFile = model.m_sourceFile; + m_sourceTimestamp = model.m_sourceTimestamp; + m_sourceRevision = model.m_sourceRevision; + m_destinationPath = model.m_destinationPath; + m_destinationFile = model.m_destinationFile; + m_destinationTimestamp = model.m_destinationTimestamp; + m_destinationRevision = model.m_destinationRevision; + m_appliedCount = model.m_appliedCount; + m_modified = model.m_modified; + + m_diffIndex = model.m_diffIndex; + m_selectedDifference = model.m_selectedDifference; + } + + return *this; +} + +bool DiffModel::operator<( const DiffModel& model ) +{ + if ( localeAwareCompareSource( model ) < 0 ) + return true; + return false; +} + +int DiffModel::localeAwareCompareSource( const DiffModel& model ) +{ + int result = m_sourcePath.localeAwareCompare( model.m_sourcePath ); + + if ( result == 0 ) + return m_sourceFile.localeAwareCompare( model.m_sourceFile ); + + return result; +} + +QString DiffModel::recreateDiff() const +{ + // For now we'll always return a diff in the diff format + QString diff; + + // recreate header + QString tab = QString::fromLatin1( "\t" ); + QString nl = QString::fromLatin1( "\n" ); + diff += QString::fromLatin1( "--- %1\t%2" ).arg( m_source ).arg( m_sourceTimestamp ); + if ( !m_sourceRevision.isEmpty() ) + diff += tab + m_sourceRevision; + diff += nl; + diff += QString::fromLatin1( "+++ %1\t%2" ).arg( m_destination ).arg( m_destinationTimestamp ); + if ( !m_destinationRevision.isEmpty() ) + diff += tab + m_destinationRevision; + diff += nl; + + // recreate body by iterating over the hunks + DiffHunkListConstIterator hunkIt = m_hunks.begin(); + DiffHunkListConstIterator hEnd = m_hunks.end(); + + for ( ; hunkIt != hEnd; ++hunkIt ) + { + if ((*hunkIt)->type() != DiffHunk::AddedByBlend) + diff += (*hunkIt)->recreateHunk(); + } + + return diff; +} + +DifferenceList* DiffModel::allDifferences() +{ + if ( m_hunks.count() != 0 ) + { + DiffHunkListConstIterator hunkIt = m_hunks.begin(); + DiffHunkListConstIterator hEnd = m_hunks.end(); + + for ( ; hunkIt != hEnd; ++hunkIt ) + { + DiffHunk* hunk = *hunkIt; + + DifferenceListConstIterator diffIt = hunk->differences().begin(); + DifferenceListConstIterator dEnd = hunk->differences().end(); + + for ( ; diffIt != dEnd; ++diffIt ) + { + m_allDifferences.append( *diffIt ); + } + } + return &m_allDifferences; + } + else + { + DifferenceList *diffList = new DifferenceList; + return diffList; + } +} + +Difference* DiffModel::firstDifference() +{ + kdDebug( 8101 ) << "DiffModel::firstDifference()" << endl; + m_diffIndex = 0; + kdDebug( 8101 ) << "m_diffIndex = " << m_diffIndex << endl; + + m_selectedDifference = m_differences[ m_diffIndex ]; + + return m_selectedDifference; +} + +Difference* DiffModel::lastDifference() +{ + kdDebug( 8101 ) << "DiffModel::lastDifference()" << endl; + m_diffIndex = m_differences.count() - 1; + kdDebug( 8101 ) << "m_diffIndex = " << m_diffIndex << endl; + + m_selectedDifference = m_differences[ m_diffIndex ]; + + return m_selectedDifference; +} + +Difference* DiffModel::prevDifference() +{ + kdDebug( 8101 ) << "DiffModel::prevDifference()" << endl; + if ( --m_diffIndex < m_differences.count() ) + { + kdDebug( 8101 ) << "m_diffIndex = " << m_diffIndex << endl; + m_selectedDifference = m_differences[ m_diffIndex ]; + } + else + { + m_selectedDifference = 0; + m_diffIndex = 0; + kdDebug( 8101 ) << "m_diffIndex = " << m_diffIndex << endl; + } + + return m_selectedDifference; +} + +Difference* DiffModel::nextDifference() +{ + kdDebug( 8101 ) << "DiffModel::nextDifference()" << endl; + if ( ++m_diffIndex < m_differences.count() ) + { + kdDebug( 8101 ) << "m_diffIndex = " << m_diffIndex << endl; + m_selectedDifference = m_differences[ m_diffIndex ]; + } + else + { + m_selectedDifference = 0; + m_diffIndex = 0; // just for safety... + kdDebug( 8101 ) << "m_diffIndex = " << m_diffIndex << endl; + } + + return m_selectedDifference; +} + +const QString DiffModel::sourceFile() const +{ + return m_sourceFile; +} + +const QString DiffModel::destinationFile() const +{ + return m_destinationFile; +} + +const QString DiffModel::sourcePath() const +{ + return m_sourcePath; +} + +const QString DiffModel::destinationPath() const +{ + return m_destinationPath; +} + +void DiffModel::setSourceFile( QString path ) +{ + m_source = path; + splitSourceInPathAndFileName(); +} + +void DiffModel::setDestinationFile( QString path ) +{ + m_destination = path; + splitDestinationInPathAndFileName(); +} + +void DiffModel::setSourceTimestamp( QString timestamp ) +{ + m_sourceTimestamp = timestamp; +} + +void DiffModel::setDestinationTimestamp( QString timestamp ) +{ + m_destinationTimestamp = timestamp; +} + +void DiffModel::setSourceRevision( QString revision ) +{ + m_destinationRevision = revision; +} + +void DiffModel::setDestinationRevision( QString revision ) +{ + m_destinationRevision = revision; +} + +void DiffModel::addHunk( DiffHunk* hunk ) +{ + m_hunks.append( hunk ); +} + +void DiffModel::addDiff( Difference* diff ) +{ + m_differences.append( diff ); +} + +void DiffModel::applyDifference( bool apply ) +{ + if ( apply && !m_selectedDifference->applied() ) + m_appliedCount++; + else if ( !apply && m_selectedDifference->applied() ) + m_appliedCount--; + + bool modified; + + // Not setting the m_modified yet so i can still query the current + // modified status from the slot that is connected to the signal + if ( m_appliedCount == 0 ) + modified = false; + else + modified = true; + + emit setModified( modified ); + + m_modified = modified; + + m_selectedDifference->apply( apply ); +} + +void DiffModel::applyAllDifferences( bool apply ) +{ + bool modified; + + // Not setting the m_modified yet so i can still query the current + // modified status from the slot that is connected to the signal + if ( apply ) + { + m_appliedCount = m_differences.count(); + modified = true; + } + else + { + m_appliedCount = 0; + modified = false; + } + + emit setModified( modified ); + + m_modified = modified; + + DifferenceListIterator diffIt = m_differences.begin(); + DifferenceListIterator dEnd = m_differences.end(); + + for ( ; diffIt != dEnd; ++diffIt ) + { + (*diffIt)->apply( apply ); + } +} + +void DiffModel::slotSetModified( bool modified ) +{ + // Not setting the m_modified yet so i can still query the current + // modified status from the slot that is connected to the signal + emit setModified( modified ); + + m_modified = modified; +} + +bool DiffModel::setSelectedDifference( Difference* diff ) +{ + kdDebug(8101) << "diff = " << diff << endl; + kdDebug(8101) << "m_selectedDifference = " << m_selectedDifference << endl; + + if ( diff != m_selectedDifference ) + { + if ( ( m_differences.findIndex( diff ) ) == -1 ) + return false; + // Dont set m_diffIndex if it cant be found + m_diffIndex = m_differences.findIndex( diff ); + kdDebug( 8101 ) << "m_diffIndex = " << m_diffIndex << endl; + m_selectedDifference = diff; + } + + return true; +} + +#include "diffmodel.moc" + +/* vim: set ts=4 sw=4 noet: */ diff --git a/kompare/libdiff2/diffmodel.h b/kompare/libdiff2/diffmodel.h new file mode 100644 index 00000000..11c424b5 --- /dev/null +++ b/kompare/libdiff2/diffmodel.h @@ -0,0 +1,150 @@ +/*************************************************************************** + diffmodel.h - description + ------------------- + begin : Sun Mar 4 2001 + copyright : (C) 2001-2004 Otto Bruggeman + (C) 2001-2003 John Firebaugh + email : otto.bruggeman@home.nl + jfirebaugh@kde.org +****************************************************************************/ + +/*************************************************************************** +** +** This program 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 2 of the License, or +** (at your option) any later version. +** +***************************************************************************/ + +#ifndef DIFFMODEL_H +#define DIFFMODEL_H + +#include <qobject.h> +#include <qstringlist.h> + +#include "diffhunk.h" +#include "kompare.h" + +namespace Diff2 +{ + +class DiffHunk; +class Difference; + +class DiffModel : public QObject +{ +Q_OBJECT +public: + + DiffModel( const QString& srcBaseURL, const QString& destBaseURL ); + DiffModel(); + DiffModel( const DiffModel& ) : QObject() {}; + ~DiffModel(); + + int parseDiff( enum Kompare::Format format, const QStringList& list ); + + QString recreateDiff() const; + + int hunkCount() const { return m_hunks.count(); } + int differenceCount() const { return m_differences.count(); } + int appliedCount() const { return m_appliedCount; } + + DiffHunk* hunkAt( int i ) { return *( m_hunks.at( i ) ); } + const Difference* differenceAt( int i ) { return *( m_differences.at( i ) ); } + + DiffHunkList* hunks() { return &m_hunks; } + const DiffHunkList* hunks() const { return &m_hunks; } + DifferenceList* differences() { return &m_differences; } + const DifferenceList* differences() const { return &m_differences; } + + DifferenceList* allDifferences(); + + int findDifference( Difference* diff ) const { return m_differences.findIndex( diff ); } + + Difference* firstDifference(); + Difference* lastDifference(); + Difference* prevDifference(); + Difference* nextDifference(); + + const QString source() const { return m_source; } + const QString destination() const { return m_destination; } + const QString sourceFile() const; + const QString destinationFile() const; + const QString sourcePath() const; + const QString destinationPath() const; + const QString sourceTimestamp() const { return m_sourceTimestamp; } + const QString destinationTimestamp() const { return m_destinationTimestamp; } + const QString sourceRevision() const { return m_sourceRevision; } + const QString destinationRevision() const { return m_destinationRevision; } + + void setSourceFile( QString path ); + void setDestinationFile( QString path ); + void setSourceTimestamp( QString timestamp ); + void setDestinationTimestamp( QString timestamp ); + void setSourceRevision( QString revision ); + void setDestinationRevision( QString revision ); + + void addHunk( DiffHunk* hunk ); + void addDiff( Difference* diff ); + bool isModified() const { return m_modified; } + + const int diffIndex( void ) const { return m_diffIndex; } + void setDiffIndex( int diffIndex ) { m_diffIndex = diffIndex; } + + void applyDifference( bool apply ); + void applyAllDifferences( bool apply ); + + bool setSelectedDifference( Difference* diff ); + + DiffModel& operator=( const DiffModel& model ); + bool operator<( const DiffModel& model ); + + int localeAwareCompareSource( const DiffModel& model ); + + bool isBlended() const { return m_blended; } + void setBlended( bool blended ) { m_blended = blended; } + +signals: + void setModified( bool modified ); + +public slots: + void slotSetModified( bool modified ); + +private: + void splitSourceInPathAndFileName(); + void splitDestinationInPathAndFileName(); + +private: + QString m_source; + QString m_destination; + + QString m_sourcePath; + QString m_destinationPath; + + QString m_sourceFile; + QString m_destinationFile; + + QString m_sourceTimestamp; + QString m_destinationTimestamp; + + QString m_sourceRevision; + QString m_destinationRevision; + + DiffHunkList m_hunks; + DifferenceList m_differences; + DifferenceList m_allDifferences; + + int m_appliedCount; + bool m_modified; + + unsigned int m_diffIndex; + Difference* m_selectedDifference; + + bool m_blended; +}; + +} // End of namespace Diff2 + +#endif + diff --git a/kompare/libdiff2/diffmodellist.cpp b/kompare/libdiff2/diffmodellist.cpp new file mode 100644 index 00000000..200e8108 --- /dev/null +++ b/kompare/libdiff2/diffmodellist.cpp @@ -0,0 +1,29 @@ +/******************************************************************************* +** +** Filename : diffmodellist.cpp +** Created on : 26 march, 2004 +** Copyright : (c) 2004 Otto Bruggeman +** Email : bruggie@home.nl +** +*******************************************************************************/ + +/******************************************************************************* +** +** This program 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 2 of the License, or +** (at your option) any later version. +** +*******************************************************************************/ + +#include <kdebug.h> + +#include "diffmodellist.h" + +using namespace Diff2; + +void DiffModelList::sort() +{ + qHeapSort(*this); +} + diff --git a/kompare/libdiff2/diffmodellist.h b/kompare/libdiff2/diffmodellist.h new file mode 100644 index 00000000..9c4f9807 --- /dev/null +++ b/kompare/libdiff2/diffmodellist.h @@ -0,0 +1,49 @@ +/******************************************************************************* +** +** Filename : diffmodellist.h +** Created on : 24 januari, 2004 +** Copyright : (c) 2004 Otto Bruggeman +** Email : bruggie@home.nl +** +*******************************************************************************/ + +/******************************************************************************* +** +** This program 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 2 of the License, or +** (at your option) any later version. +** +*******************************************************************************/ + +#ifndef DIFFMODELLIST_H +#define DIFFMODELLIST_H + +#include <qvaluelist.h> // include for the base class + +#include "diffmodel.h" + +namespace Diff2 +{ + +typedef QValueListIterator<DiffModel*> DiffModelListIterator; +typedef QValueListConstIterator<DiffModel*> DiffModelListConstIterator; + +class DiffModelList : public QValueList<DiffModel*> +{ +public: + DiffModelList() {} + DiffModelList( const DiffModelList &list ) : QValueList<DiffModel*>( list ) {} + virtual ~DiffModelList() + { + clear(); + } + +public: + virtual void sort(); + +}; // End of class DiffModelList + +} // End of Namespace Diff2 + +#endif // DIFFMODELLIST_H diff --git a/kompare/libdiff2/diffparser.cpp b/kompare/libdiff2/diffparser.cpp new file mode 100644 index 00000000..f98fbde5 --- /dev/null +++ b/kompare/libdiff2/diffparser.cpp @@ -0,0 +1,81 @@ +/************************************************************************** +** diffparser.cpp +** -------------- +** begin : Sun Aug 4 15:05:35 2002 +** copyright : (C) 2002-2004 Otto Bruggeman +** email : otto.bruggeman@home.nl +** +***************************************************************************/ +/*************************************************************************** +** +** This program 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 2 of the License, or +** ( at your option ) any later version. +** +***************************************************************************/ + +#include <qregexp.h> + +#include <kdebug.h> + +#include "diffparser.h" + +using namespace Diff2; + +DiffParser::DiffParser( const KompareModelList* list, const QStringList& diff ) : ParserBase( list, diff ) +{ + // The regexps needed for context diff parsing, the rest is the same as in parserbase.cpp + m_contextDiffHeader1.setPattern( "\\*\\*\\* ([^\\t]+)\\t([^\\t]+)\\n" ); + m_contextDiffHeader2.setPattern( "--- ([^\\t]+)\\t([^\\t]+)\\n" ); +} + +DiffParser::~DiffParser() +{ +} + +enum Kompare::Format DiffParser::determineFormat() +{ + kdDebug(8101) << "Determining the format of the diff Diff" << endl; + + QRegExp normalRE ( "[0-9]+[0-9,]*[acd][0-9]+[0-9,]*" ); + QRegExp unifiedRE( "^--- " ); + QRegExp contextRE( "^\\*\\*\\* " ); + QRegExp rcsRE ( "^[acd][0-9]+ [0-9]+" ); + QRegExp edRE ( "^[0-9]+[0-9,]*[acd]" ); + + QStringList::ConstIterator it = m_diffLines.begin(); + + while( it != m_diffLines.end() ) + { + kdDebug(8101) << (*it) << endl; + if( (*it).find( normalRE, 0 ) == 0 ) + { + kdDebug(8101) << "Difflines are from a Normal diff..." << endl; + return Kompare::Normal; + } + else if( (*it).find( unifiedRE, 0 ) == 0 ) + { + kdDebug(8101) << "Difflines are from a Unified diff..." << endl; + return Kompare::Unified; + } + else if( (*it).find( contextRE, 0 ) == 0 ) + { + kdDebug(8101) << "Difflines are from a Context diff..." << endl; + return Kompare::Context; + } + else if( (*it).find( rcsRE, 0 ) == 0 ) + { + kdDebug(8101) << "Difflines are from an RCS diff..." << endl; + return Kompare::RCS; + } + else if( (*it).find( edRE, 0 ) == 0 ) + { + kdDebug(8101) << "Difflines are from an ED diff..." << endl; + return Kompare::Ed; + } + ++it; + } + kdDebug(8101) << "Difflines are from an unknown diff..." << endl; + return Kompare::UnknownFormat; +} diff --git a/kompare/libdiff2/diffparser.h b/kompare/libdiff2/diffparser.h new file mode 100644 index 00000000..72905e3f --- /dev/null +++ b/kompare/libdiff2/diffparser.h @@ -0,0 +1,38 @@ +/************************************************************************** +** diffparser.h +** ----------------- +** begin : Sun Aug 4 15:05:35 2002 +** copyright : (C) 2002-2004 Otto Bruggeman +** email : otto.bruggeman@home.nl +** +***************************************************************************/ +/*************************************************************************** +** +** This program 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 2 of the License, or +** ( at your option ) any later version. +** +***************************************************************************/ + +#ifndef _DIFF_PARSER_H +#define _DIFF_PARSER_H + +#include "parserbase.h" + +namespace Diff2 +{ + +class DiffParser : public ParserBase +{ +public: + DiffParser( const KompareModelList* list, const QStringList& diff ); + virtual ~DiffParser(); + +protected: + virtual enum Kompare::Format determineFormat(); +}; + +} // End of namespace Diff2 + +#endif diff --git a/kompare/libdiff2/kompare.h b/kompare/libdiff2/kompare.h new file mode 100644 index 00000000..1ed5c4c7 --- /dev/null +++ b/kompare/libdiff2/kompare.h @@ -0,0 +1,144 @@ +/*************************************************************************** + kompare.h - description + ------------------- + begin : Sun Mar 4 2001 + copyright : (C) 2001-2003 by Otto Bruggeman + and John Firebaugh + email : otto.bruggeman@home.nl + jfirebaugh@kde.org +****************************************************************************/ + +/*************************************************************************** +** +** This program 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 2 of the License, or +** (at your option) any later version. +** +***************************************************************************/ + +#ifndef KOMPARE_H +#define KOMPARE_H + +#include <kurl.h> + +namespace Kompare +{ + enum Format { + Context = 0, + Ed = 1, + Normal = 2, + RCS = 3, + Unified = 4, + SideBySide = 5, + UnknownFormat = -1 + }; + + enum Generator { + CVSDiff = 0, + Diff = 1, + Perforce = 2, + SubVersion = 3, + Reserved2 = 4, + Reserved3 = 5, + Reserved4 = 6, + Reserved5 = 7, + Reserved6 = 8, + Reserved7 = 9, + UnknownGenerator = -1 + }; + + enum Mode { + ComparingFiles, // compareFiles + ComparingDirs, // compareDirs + ShowingDiff, // openDiff + BlendingDir, // openDirAnfDiff + BlendingFile, // openFileAndDiff + UnknownMode // Used to initialize the Infoi struct + }; + + enum DiffMode { + Default, + Custom, + UnknownDiffMode // Use to initialize the Info struct + }; + + enum Status { + RunningDiff, + Parsing, + FinishedParsing, + FinishedWritingDiff, + ReRunningDiff // When a change has been detected after diff has run + }; + + enum Target { + Source, + Destination + }; + + struct Info { + Info ( + enum Mode _mode = UnknownMode, + enum DiffMode _diffMode = UnknownDiffMode, + enum Format _format = UnknownFormat, + enum Generator _generator = UnknownGenerator, + KURL _source = KURL(), + KURL _destination = KURL(), + QString _localSource = "", + QString _localDestination = "" + ) + { + mode = _mode; + diffMode = _diffMode; + format = _format; + generator = _generator; + source = _source; + destination = _destination; + localSource = _localSource; + localDestination = _localDestination; + } + enum Mode mode; + enum DiffMode diffMode; + enum Format format; + enum Generator generator; + KURL source; + KURL destination; + QString localSource; + QString localDestination; + }; +} // End of namespace Kompare + +/* +** This should be removed and put somewhere else +*/ +class KompareFunctions +{ +public: + static QString constructRelativePath( const QString& from, const QString& to ) + { + KURL fromURL( from ); + KURL toURL( to ); + KURL root; + int upLevels = 0; + + // Find a common root. + root = from; + while( root.isValid() && !root.isParentOf( toURL ) ) { + root = root.upURL(); + upLevels++; + } + + if( !root.isValid() ) return to; + + QString relative; + for( ; upLevels > 0; upLevels-- ) { + relative += "../"; + } + + relative += QString( to ).replace( 0, root.path(1).length(), "" ); + + return relative; + } +}; + +#endif diff --git a/kompare/libdiff2/komparemodellist.cpp b/kompare/libdiff2/komparemodellist.cpp new file mode 100644 index 00000000..ac3c725a --- /dev/null +++ b/kompare/libdiff2/komparemodellist.cpp @@ -0,0 +1,1423 @@ +/*************************************************************************** + komparemodellist.cpp - description + ------------------- + begin : Tue Jun 26 2001 + copyright : (C) 2001-2004 Otto Bruggeman + (C) 2001-2003 John Firebaugh + (C) 2007 Kevin Kofler + email : jfirebaugh@kde.org + otto.bruggeman@home.nl + kevin.kofler@chello.at +***************************************************************************/ + +/*************************************************************************** + * * + * This program 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 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include <qfile.h> +#include <qdir.h> +#include <qregexp.h> +#include <qtextcodec.h> +#include <qvaluelist.h> + +#include <kaction.h> +#include <kcharsets.h> +#include <kdebug.h> +#include <kdirwatch.h> +#include <kio/netaccess.h> +#include <klocale.h> +#include <kmimetype.h> +#include <ktempfile.h> + +#include "difference.h" +#include "diffhunk.h" +#include "diffmodel.h" +#include "diffmodellist.h" +#include "kompareprocess.h" +#include "komparemodellist.h" +#include "parser.h" + +#include "kompare_part.h" + +using namespace Diff2; + +KompareModelList::KompareModelList( DiffSettings* diffSettings, struct Kompare::Info& info, QObject* parent, const char* name ) + : QObject( parent, name ), + m_diffProcess( 0 ), + m_diffSettings( diffSettings ), + m_models( 0 ), + m_selectedModel( 0 ), + m_selectedDifference( 0 ), + m_noOfModified( 0 ), + m_modelIndex( 0 ), + m_info( info ), + m_textCodec( 0 ) +{ + m_applyDifference = new KAction( i18n("&Apply Difference"), "1rightarrow", Qt::Key_Space, + this, SLOT(slotActionApplyDifference()), + (( KomparePart* )parent)->actionCollection(), "difference_apply" ); + m_unApplyDifference = new KAction( i18n("Un&apply Difference"), "1leftarrow", Qt::Key_BackSpace, + this, SLOT(slotActionUnApplyDifference()), + (( KomparePart* )parent)->actionCollection(), "difference_unapply" ); + m_applyAll = new KAction( i18n("App&ly All"), "2rightarrow", Qt::CTRL + Qt::Key_A, + this, SLOT(slotActionApplyAllDifferences()), + (( KomparePart* )parent)->actionCollection(), "difference_applyall" ); + m_unapplyAll = new KAction( i18n("&Unapply All"), "2leftarrow", Qt::CTRL + Qt::Key_U, + this, SLOT(slotActionUnapplyAllDifferences()), + (( KomparePart* )parent)->actionCollection(), "difference_unapplyall" ); + m_previousFile = new KAction( i18n("P&revious File"), "2uparrow", Qt::CTRL + Qt::Key_PageUp, + this, SLOT(slotPreviousModel()), + (( KomparePart* )parent)->actionCollection(), "difference_previousfile" ); + m_nextFile = new KAction( i18n("N&ext File"), "2downarrow", Qt::CTRL + Qt::Key_PageDown, + this, SLOT(slotNextModel()), + (( KomparePart* )parent)->actionCollection(), "difference_nextfile" ); + m_previousDifference = new KAction( i18n("&Previous Difference"), "1uparrow", Qt::CTRL + Qt::Key_Up, + this, SLOT(slotPreviousDifference()), + (( KomparePart* )parent)->actionCollection(), "difference_previous" ); + m_nextDifference = new KAction( i18n("&Next Difference"), "1downarrow", Qt::CTRL + Qt::Key_Down, + this, SLOT(slotNextDifference()), + (( KomparePart* )parent)->actionCollection(), "difference_next" ); + m_previousDifference->setEnabled( false ); + m_nextDifference->setEnabled( false ); + + m_save = KStdAction::save( this, SLOT(slotSaveDestination()), ((KomparePart*)parent)->actionCollection() ); + m_save->setEnabled( false ); + + updateModelListActions(); +} + +KompareModelList::~KompareModelList() +{ +} + +bool KompareModelList::isDirectory( const QString& url ) const +{ + QFileInfo fi( url ); + if ( fi.isDir() ) + return true; + else + return false; +} + +bool KompareModelList::isDiff( const QString& mimeType ) const +{ + if ( mimeType == "text/x-diff" ) + return true; + else + return false; +} + +bool KompareModelList::compare( const QString& source, const QString& destination ) +{ + bool result = false; + + bool sourceIsDirectory = isDirectory( source ); + bool destinationIsDirectory = isDirectory( source ); + + if ( sourceIsDirectory && destinationIsDirectory ) + { + m_info.mode = Kompare::ComparingDirs; + result = compareDirs( source, destination ); + } + else if ( !sourceIsDirectory && !destinationIsDirectory ) + { + QFile sourceFile( source ); + sourceFile.open( IO_ReadOnly ); + QString sourceMimeType = ( KMimeType::findByContent( sourceFile.readAll() ) )->name(); + sourceFile.close(); + kdDebug(8101) << "Mimetype source : " << sourceMimeType << endl; + + QFile destinationFile( destination ); + destinationFile.open( IO_ReadOnly ); + QString destinationMimeType = ( KMimeType::findByContent( destinationFile.readAll() ) )->name(); + destinationFile.close(); + kdDebug(8101) << "Mimetype destination: " << destinationMimeType << endl; + + // Not checking if it is a text file/something diff can even compare, we'll let diff handle that + if ( !isDiff( sourceMimeType ) && isDiff( destinationMimeType ) ) + { + kdDebug(8101) << "Blending destination into source..." << endl; + m_info.mode = Kompare::BlendingFile; + result = openFileAndDiff( source, destination ); + } + else if ( isDiff( sourceMimeType ) && !isDiff( destinationMimeType ) ) + { + kdDebug(8101) << "Blending source into destination..." << endl; + m_info.mode = Kompare::BlendingFile; + result = openFileAndDiff( destination, source ); + } + else + { + kdDebug(8101) << "Comparing source with destination" << endl; + m_info.mode = Kompare::ComparingFiles; + result = compareFiles( source, destination ); + } + } + else if ( sourceIsDirectory && !destinationIsDirectory ) + { + m_info.mode = Kompare::BlendingDir; + result = openDirAndDiff( source, destination ); + } + else + { + m_info.mode = Kompare::BlendingDir; + result = openDirAndDiff( destination, source ); + } + + return result; +} + +bool KompareModelList::compareFiles( const QString& source, const QString& destination ) +{ + m_source = source; + m_destination = destination; + + clear(); // Destroy the old models... + +// m_fileWatch = new KDirWatch( this, "filewatch" ); +// m_fileWatch->addFile( m_source ); +// m_fileWatch->addFile( m_destination ); + +// connect( m_fileWatch, SIGNAL( dirty( const QString& ) ), this, SLOT( slotFileChanged( const QString& ) ) ); +// connect( m_fileWatch, SIGNAL( created( const QString& ) ), this, SLOT( slotFileChanged( const QString& ) ) ); +// connect( m_fileWatch, SIGNAL( deleted( const QString& ) ), this, SLOT( slotFileChanged( const QString& ) ) ); + +// m_fileWatch->startScan(); + m_diffProcess = new KompareProcess( m_diffSettings, Kompare::Custom, m_source, m_destination ); + m_diffProcess->setEncoding( m_encoding ); + + connect( m_diffProcess, SIGNAL(diffHasFinished( bool )), + this, SLOT(slotDiffProcessFinished( bool )) ); + + emit status( Kompare::RunningDiff ); + m_diffProcess->start(); + + return true; +} + +bool KompareModelList::compareDirs( const QString& source, const QString& destination ) +{ + m_source = source; + m_destination = destination; + + clear(); // Destroy the old models... + +// m_dirWatch = new KDirWatch( this, "dirwatch" ); + // Watch files in the dirs and watch the dirs recursively +// m_dirWatch->addDir( m_source, true, true ); +// m_dirWatch->addDir( m_destination, true, true ); + +// connect( m_dirWatch, SIGNAL( dirty ( const QString& ) ), this, SLOT( slotDirectoryChanged( const QString& ) ) ); +// connect( m_dirWatch, SIGNAL( created( const QString& ) ), this, SLOT( slotDirectoryChanged( const QString& ) ) ); +// connect( m_dirWatch, SIGNAL( deleted( const QString& ) ), this, SLOT( slotDirectoryChanged( const QString& ) ) ); + +// m_dirWatch->startScan(); + m_diffProcess = new KompareProcess( m_diffSettings, Kompare::Custom, m_source, m_destination ); + m_diffProcess->setEncoding( m_encoding ); + + connect( m_diffProcess, SIGNAL(diffHasFinished( bool )), + this, SLOT(slotDiffProcessFinished( bool )) ); + + emit status( Kompare::RunningDiff ); + m_diffProcess->start(); + + return true; +} + +bool KompareModelList::openFileAndDiff( const QString& file, const QString& diff ) +{ + clear(); + + if ( parseDiffOutput( readFile( diff ) ) != 0 ) + { + emit error( i18n( "<qt>No models or no differences, this file: <b>%1</b>, is not a valid diff file.</qt>" ).arg( diff ) ); + return false; + } + + // Do our thing :) + if ( !blendOriginalIntoModelList( file ) ) + { + kdDebug(8101) << "Oops cant blend original file into modellist : " << file << endl; + emit( i18n( "<qt>There were problems applying the diff <b>%1</b> to the file <b>%2</b>.</qt>" ).arg( diff ).arg( file ) ); + return false; + } + + updateModelListActions(); + show(); + + return true; +} + +bool KompareModelList::openDirAndDiff( const QString& dir, const QString& diff ) +{ + clear(); + + if ( parseDiffOutput( readFile( diff ) ) != 0 ) + { + emit error( i18n( "<qt>No models or no differences, this file: <b>%1</b>, is not a valid diff file.</qt>" ).arg( diff ) ); + return false; + } + + // Do our thing :) + if ( !blendOriginalIntoModelList( dir ) ) + { + // Trouble blending the original into the model + kdDebug(8101) << "Oops cant blend original dir into modellist : " << dir << endl; + emit error( i18n( "<qt>There were problems applying the diff <b>%1</b> to the folder <b>%2</b>.</qt>" ).arg( diff ).arg( dir ) ); + return false; + } + + updateModelListActions(); + show(); + + return true; +} + +void KompareModelList::slotSaveDestination() +{ + if ( m_selectedModel ) + { + saveDestination( m_selectedModel ); + } +} + +bool KompareModelList::saveDestination( DiffModel* model ) +{ + kdDebug() << "KompareModelList::saveDestination: " << endl; + + if( !model->isModified() ) + return true; + + KTempFile* temp = new KTempFile(); + + if( temp->status() != 0 ) { + emit error( i18n( "Could not open a temporary file." ) ); + temp->unlink(); + delete temp; + return false; + } + + QTextStream* stream = temp->textStream(); + QStringList list; + + DiffHunkListConstIterator hunkIt = model->hunks()->begin(); + DiffHunkListConstIterator hEnd = model->hunks()->end(); + + for( ; hunkIt != hEnd; ++hunkIt ) + { + DiffHunk* hunk = *hunkIt; + + DifferenceListConstIterator diffIt = hunk->differences().begin(); + DifferenceListConstIterator dEnd = hunk->differences().end(); + + Difference* diff; + for( ; diffIt != dEnd; ++diffIt ) + { + diff = *diffIt; + if( !diff->applied() ) + { + DifferenceStringListConstIterator stringIt = diff->destinationLines().begin(); + DifferenceStringListConstIterator sEnd = diff->destinationLines().end(); + for ( ; stringIt != sEnd; ++stringIt ) + { + list.append( ( *stringIt )->string() ); + } + } + else + { + DifferenceStringListConstIterator stringIt = diff->sourceLines().begin(); + DifferenceStringListConstIterator sEnd = diff->sourceLines().end(); + for ( ; stringIt != sEnd; ++stringIt ) + { + list.append( ( *stringIt )->string() ); + } + } + } + } + + // kdDebug( 8101 ) << "Everything: " << endl << list.join( "\n" ) << endl; + + if( list.count() > 0 ) + *stream << list.join( "" ); + + temp->close(); + if( temp->status() != 0 ) { + emit error( i18n( "<qt>Could not write to the temporary file <b>%1</b>, deleting it.</qt>" ).arg( temp->name() ) ); + temp->unlink(); + delete temp; + return false; + } + + bool result = false; + + if ( m_info.mode == Kompare::ComparingDirs ) + { + QString destination = model->destinationPath() + model->destinationFile(); + kdDebug(8101) << "Tempfilename : " << temp->name() << endl; + kdDebug(8101) << "DestinationURL : " << destination << endl; + KIO::UDSEntry entry; + if ( !KIO::NetAccess::stat( KURL( destination ).path(), entry, (QWidget*)parent() ) ) + { + if ( !KIO::NetAccess::mkdir( KURL( destination ).path(), (QWidget*)parent() ) ) + { + emit error( i18n( "<qt>Could not create destination directory <b>%1</b>.\nThe file has not been saved.</qt>" ) ); + return false; + } + } + result = KIO::NetAccess::upload( temp->name(), KURL( destination ), (QWidget*)parent() ); + } + else + { + kdDebug(8101) << "Tempfilename : " << temp->name() << endl; + kdDebug(8101) << "DestinationURL : " << m_destination << endl; + result = KIO::NetAccess::upload( temp->name(), KURL( m_destination ), (QWidget*)parent() ); + } + + if ( !result ) + { + emit error( i18n( "<qt>Could not upload the temporary file to the destination location <b>%1</b>. The temporary file is still available under: <b>%2</b>. You can manually copy it to the right place.</qt>" ).arg( m_destination ).arg( temp->name() ) ); + } + else + { + //model->slotSetModified( false ); + temp->unlink(); + delete temp; + } + + return true; +} + +bool KompareModelList::saveAll() +{ + if ( !m_models ) + return false; + + DiffModelListIterator it = m_models->begin(); + DiffModelListIterator end = m_models->end(); + for ( ; it != end; ++it ) + { + if( !saveDestination( *it ) ) + return false; + } + return true; +} + +void KompareModelList::setEncoding( const QString& encoding ) +{ + m_encoding = encoding; + if ( encoding.lower() == "default" ) + { + m_textCodec = QTextCodec::codecForLocale(); + } + else + { + kdDebug() << "Encoding : " << encoding << endl; + m_textCodec = KGlobal::charsets()->codecForName( encoding.latin1() ); + kdDebug() << "TextCodec: " << m_textCodec << endl; + if ( !m_textCodec ) + m_textCodec = QTextCodec::codecForLocale(); + } + kdDebug() << "TextCodec: " << m_textCodec << endl; +} + +void KompareModelList::slotDiffProcessFinished( bool success ) +{ + if ( success ) + { + emit status( Kompare::Parsing ); + if ( parseDiffOutput( m_diffProcess->diffOutput() ) != 0 ) + { + emit error( i18n( "Could not parse diff output." ) ); + } + else + { + if ( m_info.mode != Kompare::ShowingDiff ) + { + kdDebug() << "Blend this crap please and dont gimme any conflicts..." << endl; + blendOriginalIntoModelList( m_info.localSource ); + } + updateModelListActions(); + show(); + } + emit status( Kompare::FinishedParsing ); + } + else if ( m_diffProcess->exitStatus() == 0 ) + { + emit error( i18n( "The files are identical." ) ); + } + else + { + emit error( m_diffProcess->stdErr() ); + } + + delete m_diffProcess; + m_diffProcess = 0; +} + +void KompareModelList::slotDirectoryChanged( const QString& /*dir*/ ) +{ + // some debug output to see if watching works properly + kdDebug(8101) << "Yippie directories are being watched !!! :)" << endl; + if ( m_diffProcess ) + { + emit status( Kompare::ReRunningDiff ); + m_diffProcess->start(); + } +} + +void KompareModelList::slotFileChanged( const QString& /*file*/ ) +{ + // some debug output to see if watching works properly + kdDebug(8101) << "Yippie files are being watched !!! :)" << endl; + if ( m_diffProcess ) + { + emit status( Kompare::ReRunningDiff ); + m_diffProcess->start(); + } +} + +QStringList KompareModelList::split( const QString& fileContents ) +{ + QString contents = fileContents; + QStringList list; + + int pos = 0; + unsigned int oldpos = 0; + // split that does not strip the split char +#ifdef QT_OS_MAC + const char split = '\r'; +#else + const char split = '\n'; +#endif + while ( ( pos = contents.find( split, oldpos ) ) >= 0 ) + { + list.append( contents.mid( oldpos, pos - oldpos + 1 ) ); + oldpos = pos + 1; + } + + if ( contents.length() > oldpos ) + { + list.append( contents.right( contents.length() - oldpos ) ); + } + + return list; +} + +QString KompareModelList::readFile( const QString& fileName ) +{ + QStringList list; + + QFile file( fileName ); + file.open( IO_ReadOnly ); + + QTextStream stream( &file ); + kdDebug() << "Codec = " << m_textCodec << endl; + + if ( !m_textCodec ) + m_textCodec = QTextCodec::codecForLocale(); + + stream.setCodec( m_textCodec ); + + QString contents = stream.read(); + + file.close(); + + return contents; +} + +bool KompareModelList::openDiff( const QString& diffFile ) +{ + kdDebug(8101) << "Stupid :) Url = " << diffFile << endl; + + if ( diffFile.isEmpty() ) + return false; + + QString diff = readFile( diffFile ); + + clear(); // Clear the current models + + emit status( Kompare::Parsing ); + + if ( parseDiffOutput( diff ) != 0 ) + { + emit error( i18n( "Could not parse diff output." ) ); + return false; + } + + updateModelListActions(); + show(); + + emit status( Kompare::FinishedParsing ); + + return true; +} + +QString KompareModelList::recreateDiff() const +{ + QString diff; + + DiffModelListConstIterator modelIt = m_models->begin(); + DiffModelListConstIterator mEnd = m_models->end(); + + for ( ; modelIt != mEnd; ++modelIt ) + { + diff += (*modelIt)->recreateDiff(); + } + return diff; +} + +bool KompareModelList::saveDiff( const QString& url, QString directory, DiffSettings* diffSettings ) +{ + kdDebug() << "KompareModelList::saveDiff: " << endl; + + m_diffTemp = new KTempFile(); + m_diffURL = url; + + if( m_diffTemp->status() != 0 ) { + emit error( i18n( "Could not open a temporary file." ) ); + m_diffTemp->unlink(); + delete m_diffTemp; + m_diffTemp = 0; + return false; + } + + m_diffProcess = new KompareProcess( diffSettings, Kompare::Custom, m_source, m_destination, directory ); + m_diffProcess->setEncoding( m_encoding ); + + connect( m_diffProcess, SIGNAL(diffHasFinished( bool )), + this, SLOT(slotWriteDiffOutput( bool )) ); + + emit status( Kompare::RunningDiff ); + return m_diffProcess->start(); +} + +void KompareModelList::slotWriteDiffOutput( bool success ) +{ + kdDebug(8101) << "Success = " << success << endl; + + if( success ) + { + QTextStream* stream = m_diffTemp->textStream(); + + *stream << m_diffProcess->diffOutput(); + + m_diffTemp->close(); + + if( m_diffTemp->status() != 0 ) + { + emit error( i18n( "Could not write to the temporary file." ) ); + } + + KIO::NetAccess::upload( m_diffTemp->name(), KURL( m_diffURL ), (QWidget*)parent() ); + + emit status( Kompare::FinishedWritingDiff ); + } + + m_diffURL.truncate( 0 ); + m_diffTemp->unlink(); + + delete m_diffTemp; + m_diffTemp = 0; + + delete m_diffProcess; + m_diffProcess = 0; +} + +void KompareModelList::slotSelectionChanged( const Diff2::DiffModel* model, const Diff2::Difference* diff ) +{ +// This method will signal all the other objects about a change in selection, +// it will emit setSelection( const DiffModel*, const Difference* ) to all who are connected + kdDebug(8101) << "KompareModelList::slotSelectionChanged( " << model << ", " << diff << " )" << endl; + kdDebug(8101) << "Sender is : " << sender()->className() << endl; +// kdDebug(8101) << kdBacktrace() << endl; + + m_selectedModel = const_cast<DiffModel*>(model); + m_modelIndex = m_models->findIndex( m_selectedModel ); + kdDebug( 8101 ) << "m_modelIndex = " << m_modelIndex << endl; + m_selectedDifference = const_cast<Difference*>(diff); + + m_selectedModel->setSelectedDifference( m_selectedDifference ); + + // setSelected* search for the argument in the lists and return false if not found + // if found they return true and set the m_selected* + if ( !setSelectedModel( m_selectedModel ) ) + { + // Backup plan + m_selectedModel = firstModel(); + m_selectedDifference = m_selectedModel->firstDifference(); + } + else if ( !m_selectedModel->setSelectedDifference( m_selectedDifference ) ) + { + // Another backup plan + m_selectedDifference = m_selectedModel->firstDifference(); + } + + emit setSelection( model, diff ); + emit setStatusBarModelInfo( findModel( m_selectedModel ), m_selectedModel->findDifference( m_selectedDifference ), modelCount(), differenceCount(), m_selectedModel->appliedCount() ); + + updateModelListActions(); +} + +void KompareModelList::slotSelectionChanged( const Diff2::Difference* diff ) +{ +// This method will emit setSelection( const Difference* ) to whomever is listening +// when for instance in kompareview the selection has changed + kdDebug(8101) << "KompareModelList::slotSelectionChanged( " << diff << " )" << endl; + kdDebug(8101) << "Sender is : " << sender()->className() << endl; + + m_selectedDifference = const_cast<Difference*>(diff); + + if ( !m_selectedModel->setSelectedDifference( m_selectedDifference ) ) + { + // Backup plan + m_selectedDifference = m_selectedModel->firstDifference(); + } + + emit setSelection( diff ); + emit setStatusBarModelInfo( findModel( m_selectedModel ), m_selectedModel->findDifference( m_selectedDifference ), modelCount(), differenceCount(), m_selectedModel->appliedCount() ); + + updateModelListActions(); +} + +void KompareModelList::slotPreviousModel() +{ + if ( ( m_selectedModel = prevModel() ) != 0 ) + { + m_selectedDifference = m_selectedModel->firstDifference(); + } + else + { + m_selectedModel = firstModel(); + m_selectedDifference = m_selectedModel->firstDifference(); + } + + emit setSelection( m_selectedModel, m_selectedDifference ); + emit setStatusBarModelInfo( findModel( m_selectedModel ), m_selectedModel->findDifference( m_selectedDifference ), modelCount(), differenceCount(), m_selectedModel->appliedCount() ); + updateModelListActions(); +} + +void KompareModelList::slotNextModel() +{ + if ( ( m_selectedModel = nextModel() ) != 0 ) + { + m_selectedDifference = m_selectedModel->firstDifference(); + } + else + { + m_selectedModel = lastModel(); + m_selectedDifference = m_selectedModel->firstDifference(); + } + + emit setSelection( m_selectedModel, m_selectedDifference ); + emit setStatusBarModelInfo( findModel( m_selectedModel ), m_selectedModel->findDifference( m_selectedDifference ), modelCount(), differenceCount(), m_selectedModel->appliedCount() ); + updateModelListActions(); +} + +DiffModel* KompareModelList::firstModel() +{ + kdDebug( 8101 ) << "KompareModelList::firstModel()" << endl; + m_modelIndex = 0; + kdDebug( 8101 ) << "m_modelIndex = " << m_modelIndex << endl; + + m_selectedModel = m_models->first(); + + return m_selectedModel; +} + +DiffModel* KompareModelList::lastModel() +{ + kdDebug( 8101 ) << "KompareModelList::lastModel()" << endl; + m_modelIndex = m_models->count() - 1; + kdDebug( 8101 ) << "m_modelIndex = " << m_modelIndex << endl; + + m_selectedModel = m_models->last(); + + return m_selectedModel; +} + +DiffModel* KompareModelList::prevModel() +{ + kdDebug( 8101 ) << "KompareModelList::prevModel()" << endl; + if ( --m_modelIndex < m_models->count() ) + { + kdDebug( 8101 ) << "m_modelIndex = " << m_modelIndex << endl; + m_selectedModel = (*m_models)[ m_modelIndex ]; + } + else + { + m_selectedModel = 0; + m_modelIndex = 0; + kdDebug( 8101 ) << "m_modelIndex = " << m_modelIndex << endl; + } + + return m_selectedModel; +} + +DiffModel* KompareModelList::nextModel() +{ + kdDebug( 8101 ) << "KompareModelList::nextModel()" << endl; + if ( ++m_modelIndex < m_models->count() ) + { + kdDebug( 8101 ) << "m_modelIndex = " << m_modelIndex << endl; + m_selectedModel = (*m_models)[ m_modelIndex ]; + } + else + { + m_selectedModel = 0; + m_modelIndex = 0; + kdDebug( 8101 ) << "m_modelIndex = " << m_modelIndex << endl; + } + + return m_selectedModel; +} + +void KompareModelList::slotPreviousDifference() +{ + kdDebug(8101) << "slotPreviousDifference called" << endl; + if ( ( m_selectedDifference = m_selectedModel->prevDifference() ) != 0 ) + { + emit setSelection( m_selectedDifference ); + emit setStatusBarModelInfo( findModel( m_selectedModel ), m_selectedModel->findDifference( m_selectedDifference ), modelCount(), differenceCount(), m_selectedModel->appliedCount() ); + updateModelListActions(); + return; + } + + kdDebug(8101) << "**** no previous difference... ok lets find the previous model..." << endl; + + if ( ( m_selectedModel = prevModel() ) != 0 ) + { + m_selectedDifference = m_selectedModel->lastDifference(); + + emit setSelection( m_selectedModel, m_selectedDifference ); + emit setStatusBarModelInfo( findModel( m_selectedModel ), m_selectedModel->findDifference( m_selectedDifference ), modelCount(), differenceCount(), m_selectedModel->appliedCount() ); + updateModelListActions(); + return; + } + + + kdDebug(8101) << "**** !!! No previous model, ok backup plan activated..." << endl; + + // Backup plan + m_selectedModel = firstModel(); + m_selectedDifference = m_selectedModel->firstDifference(); + + emit setSelection( m_selectedModel, m_selectedDifference ); + emit setStatusBarModelInfo( findModel( m_selectedModel ), m_selectedModel->findDifference( m_selectedDifference ), modelCount(), differenceCount(), m_selectedModel->appliedCount() ); + updateModelListActions(); +} + +void KompareModelList::slotNextDifference() +{ + kdDebug(8101) << "slotNextDifference called" << endl; + if ( ( m_selectedDifference = m_selectedModel->nextDifference() ) != 0 ) + { + emit setSelection( m_selectedDifference ); + emit setStatusBarModelInfo( findModel( m_selectedModel ), m_selectedModel->findDifference( m_selectedDifference ), modelCount(), differenceCount(), m_selectedModel->appliedCount() ); + updateModelListActions(); + return; + } + + kdDebug(8101) << "**** no next difference... ok lets find the next model..." << endl; + + if ( ( m_selectedModel = nextModel() ) != 0 ) + { + m_selectedDifference = m_selectedModel->firstDifference(); + + emit setSelection( m_selectedModel, m_selectedDifference ); + emit setStatusBarModelInfo( findModel( m_selectedModel ), m_selectedModel->findDifference( m_selectedDifference ), modelCount(), differenceCount(), m_selectedModel->appliedCount() ); + updateModelListActions(); + return; + } + + kdDebug(8101) << "**** !!! No next model, ok backup plan activated..." << endl; + + // Backup plan + m_selectedModel = lastModel(); + m_selectedDifference = m_selectedModel->lastDifference(); + + emit setSelection( m_selectedModel, m_selectedDifference ); + emit setStatusBarModelInfo( findModel( m_selectedModel ), m_selectedModel->findDifference( m_selectedDifference ), modelCount(), differenceCount(), m_selectedModel->appliedCount() ); + updateModelListActions(); +} + +void KompareModelList::slotApplyDifference( bool apply ) +{ + m_selectedModel->applyDifference( apply ); + emit applyDifference( apply ); +} + +void KompareModelList::slotApplyAllDifferences( bool apply ) +{ + m_selectedModel->applyAllDifferences( apply ); + emit applyAllDifferences( apply ); +} + +int KompareModelList::parseDiffOutput( const QString& diff ) +{ + kdDebug(8101) << "KompareModelList::parseDiffOutput" << endl; + + QStringList diffLines = split( diff ); + + Parser* parser = new Parser( this ); + m_models = parser->parse( diffLines ); + + m_info.generator = parser->generator(); + m_info.format = parser->format(); + + delete parser; + + if ( m_models ) + { + m_selectedModel = firstModel(); + kdDebug(8101) << "Ok there are differences..." << endl; + m_selectedDifference = m_selectedModel->firstDifference(); + emit setStatusBarModelInfo( 0, 0, modelCount(), differenceCount(), 0); + } + else + { + // Wow trouble, no models, so no differences... + kdDebug(8101) << "Now i'll be damned, there should be models here !!!" << endl; + return -1; + } + + return 0; +} + +bool KompareModelList::blendOriginalIntoModelList( const QString& localURL ) +{ + kdDebug() << "Hurrah we are blending..." << endl; + QFileInfo fi( localURL ); + + bool result = false; + DiffModel* model; + + QString fileContents; + + if ( fi.isDir() ) + { // is a dir + kdDebug() << "Blend Dir" << endl; +// QDir dir( localURL, QString::null, QDir::Name|QDir::DirsFirst, QDir::All ); + DiffModelListIterator modelIt = m_models->begin(); + DiffModelListIterator mEnd = m_models->end(); + for ( ; modelIt != mEnd; ++modelIt ) + { + model = *modelIt; + kdDebug(8101) << "Model : " << model << endl; + QString filename = model->sourcePath() + model->sourceFile(); + if ( !filename.startsWith( localURL ) ) + filename.prepend( localURL ); + QFileInfo fi2( filename ); + if ( fi2.exists() ) + { + kdDebug(8101) << "Reading from: " << filename << endl; + fileContents = readFile( filename ); + result = blendFile( model, fileContents ); + } + else + { + kdDebug(8101) << "File " << filename << " does not exist !" << endl; + kdDebug(8101) << "Assume empty file !" << endl; + fileContents.truncate( 0 ); + result = blendFile( model, fileContents ); + } + } + kdDebug() << "End of Blend Dir" << endl; + } + else if ( fi.isFile() ) + { // is a file + kdDebug() << "Blend File" << endl; + kdDebug(8101) << "Reading from: " << localURL << endl; + fileContents = readFile( localURL ); + + result = blendFile( (*m_models)[ 0 ], fileContents ); + kdDebug() << "End of Blend File" << endl; + } + + return result; +} + +bool KompareModelList::blendFile( DiffModel* model, const QString& fileContents ) +{ + if ( !model ) + { + kdDebug() << "**** model is null :(" << endl; + return false; + } + + model->setBlended( true ); + + int srcLineNo = 1, destLineNo = 1; + + QStringList lines = split( fileContents ); + + QStringList::ConstIterator linesIt = lines.begin(); + QStringList::ConstIterator lEnd = lines.end(); + + DiffHunkList* hunks = model->hunks(); + kdDebug(8101) << "Hunks in hunklist: " << hunks->count() << endl; + DiffHunkListIterator hunkIt = hunks->begin(); + + DiffHunk* newHunk = 0; + Difference* newDiff = 0; + + // FIXME: this approach is not very good, we should first check if the hunk applies cleanly + // and without offset and if not use that new linenumber with offset to compare against + // This will only work for files we just diffed with kompare but not for blending where + // file(s) to patch has/have potentially changed + + for ( ; hunkIt != hunks->end(); ++hunkIt ) + { + // Do we need to insert a new hunk before this one ? + DiffHunk* hunk = *hunkIt; + if ( srcLineNo < hunk->sourceLineNumber() ) + { + newHunk = new DiffHunk( srcLineNo, destLineNo, "", DiffHunk::AddedByBlend ); + + hunks->insert( hunkIt, newHunk ); + + newDiff = new Difference( srcLineNo, destLineNo, + Difference::Unchanged ); + + newHunk->add( newDiff ); + + while ( srcLineNo < hunk->sourceLineNumber() && linesIt != lEnd ) + { + newDiff->addSourceLine( *linesIt ); + newDiff->addDestinationLine( *linesIt ); + srcLineNo++; + destLineNo++; + ++linesIt; + } + } + + // Now we add the linecount difference for the hunk that follows + int size = hunk->sourceLineCount(); + + for ( int i = 0; i < size; ++i ) + { + ++linesIt; + } + + srcLineNo += size; + destLineNo += (*hunkIt)->destinationLineCount(); + } + + if ( linesIt != lEnd ) + { + newHunk = new DiffHunk( srcLineNo, destLineNo, "", DiffHunk::AddedByBlend ); + + model->addHunk( newHunk ); + + newDiff = new Difference( srcLineNo, destLineNo, Difference::Unchanged ); + + newHunk->add( newDiff ); + + while ( linesIt != lEnd ) + { + newDiff->addSourceLine( *linesIt ); + newDiff->addDestinationLine( *linesIt ); + ++linesIt; + } + } +#if 0 + DifferenceList hunkDiffList = (*hunkIt)->differences(); + DifferenceListIterator diffIt = hunkDiffList.begin(); + DifferenceListIterator dEnd = hunkDiffList.end(); + kdDebug() << "Number of differences in hunkDiffList = " << diffList.count() << endl; + + DifferenceListIterator tempIt; + Difference* diff; + + for ( ; diffIt != dEnd; ++diffIt ) + { + diff = *diffIt; + kdDebug() << "*(Diff it) = " << diff << endl; + // Check if there are lines in the original file before the difference + // that are not yet in the diff. If so create new Unchanged diff + if ( srcLineNo < diff->sourceLineNumber() ) + { + newDiff = new Difference( srcLineNo, destLineNo, + Difference::Unchanged | Difference::AddedByBlend ); + newHunk->add( newDiff ); + while ( srcLineNo < diff->sourceLineNumber() && linesIt != lEnd ) + { +// kdDebug(8101) << "SourceLine = " << srcLineNo << ": " << *linesIt << endl; + newDiff->addSourceLine( *linesIt ); + newDiff->addDestinationLine( *linesIt ); + srcLineNo++; + destLineNo++; + ++linesIt; + } + } + // Now i've got to add that diff + switch ( diff->type() ) + { + case Difference::Unchanged: + kdDebug(8101) << "Unchanged" << endl; + for ( int i = 0; i < diff->sourceLineCount(); i++ ) + { + if ( linesIt != lEnd && *linesIt != diff->sourceLineAt( i )->string() ) + { + kdDebug(8101) << "Conflict: SourceLine = " << srcLineNo << ": " << *linesIt << endl; + kdDebug(8101) << "Conflict: DiffLine = " << diff->sourceLineNumber() + i << ": " << diff->sourceLineAt( i )->string() << endl; + + // Do conflict resolution (well sort of) + diff->sourceLineAt( i )->setConflictString( *linesIt ); + diff->setConflict( true ); + } +// kdDebug(8101) << "SourceLine = " << srcLineNo << ": " << *linesIt << endl; +// kdDebug(8101) << "DiffLine = " << diff->sourceLineNumber() + i << ": " << diff->sourceLineAt( i )->string() << endl; + srcLineNo++; + destLineNo++; + ++linesIt; + } + + tempIt = diffIt; + --diffIt; + diffList.remove( tempIt ); + newHunk->add( diff ); + + break; + case Difference::Change: + kdDebug(8101) << "Change" << endl; + + //QStringListConstIterator saveIt = linesIt; + + for ( int i = 0; i < diff->sourceLineCount(); i++ ) + { + if ( linesIt != lEnd && *linesIt != diff->sourceLineAt( i )->string() ) + { + kdDebug(8101) << "Conflict: SourceLine = " << srcLineNo << ": " << *linesIt << endl; + kdDebug(8101) << "Conflict: DiffLine = " << diff->sourceLineNumber() + i << ": " << diff->sourceLineAt( i )->string() << endl; + + // Do conflict resolution (well sort of) + diff->sourceLineAt( i )->setConflictString( *linesIt ); + diff->setConflict( true ); + } + srcLineNo++; + destLineNo++; + ++linesIt; + } + + destLineNo += diff->destinationLineCount(); + + tempIt = diffIt; + --diffIt; + diffList.remove( tempIt ); + newHunk->add( diff ); + newModel->addDiff( diff ); + + break; + case Difference::Insert: + kdDebug(8101) << "Insert" << endl; + destLineNo += diff->destinationLineCount(); + tempIt = diffIt; + --diffIt; + diffList.remove( tempIt ); + newHunk->add( diff ); + newModel->addDiff( diff ); + break; + case Difference::Delete: + kdDebug(8101) << "Delete" << endl; + kdDebug(8101) << "Number of lines in Delete: " << diff->sourceLineCount() << endl; + for ( int i = 0; i < diff->sourceLineCount(); i++ ) + { + if ( linesIt != lEnd && *linesIt != diff->sourceLineAt( i )->string() ) + { + kdDebug(8101) << "Conflict: SourceLine = " << srcLineNo << ": " << *linesIt << endl; + kdDebug(8101) << "Conflict: DiffLine = " << diff->sourceLineNumber() + i << ": " << diff->sourceLineAt( i )->string() << endl; + + // Do conflict resolution (well sort of) + diff->sourceLineAt( i )->setConflictString( *linesIt ); + diff->setConflict( true ); + } + +// kdDebug(8101) << "SourceLine = " << srcLineNo << ": " << *it << endl; +// kdDebug(8101) << "DiffLine = " << diff->sourceLineNumber() + i << ": " << diff->sourceLineAt( i )->string() << endl; + srcLineNo++; + ++linesIt; + } + + tempIt = diffIt; + --diffIt; + diffList.remove( tempIt ); + newHunk->add( diff ); + newModel->addDiff( diff ); + break; + default: + kdDebug(8101) << "****, some diff type we dont know about ???" << endl; + } + } + } +#endif + +/* + diffList = newModel->differences(); + + diff = diffList.first(); + kdDebug(8101) << "Count = " << diffList.count() << endl; + for ( diff = diffList.first(); diff; diff = diffList.next() ) + { + kdDebug(8101) << "sourcelinenumber = " << diff->sourceLineNumber() << endl; + } +*/ + + m_selectedModel = firstModel(); + + m_selectedDifference = m_selectedModel->firstDifference(); + + return true; +} + +void KompareModelList::show() +{ + kdDebug() << "KompareModelList::Show Number of models = " << m_models->count() << endl; + emit modelsChanged( m_models ); + emit setSelection( m_selectedModel, m_selectedDifference ); +} + +void KompareModelList::clear() +{ + if ( m_models ) + m_models->clear(); + + emit modelsChanged( m_models ); +} + +void KompareModelList::swap() +{ + QString source = m_source; + QString destination = m_destination; + if ( m_info.mode == Kompare::ComparingFiles ) + compareFiles( destination, source ); + else if ( m_info.mode == Kompare::ComparingDirs ) + compareDirs( destination, source ); +} + +bool KompareModelList::isModified() const +{ + if ( m_noOfModified > 0 ) + return true; + return false; +} + +int KompareModelList::modelCount() const +{ + return m_models ? m_models->count() : 0; +} + +int KompareModelList::differenceCount() const +{ + return m_selectedModel ? m_selectedModel->differenceCount() : -1; +} + +int KompareModelList::appliedCount() const +{ + return m_selectedModel ? m_selectedModel->appliedCount() : -1; +} + +void KompareModelList::slotSetModified( bool modified ) +{ + kdDebug(8101) << "KompareModelList::slotSetModified( " << modified << " );" << endl; + kdDebug(8101) << "Before: m_noOfModified = " << m_noOfModified << endl; + + // If selectedModel emits its signal setModified it does not set the model + // internal m_modified bool yet, it only does that after the emit. + if ( modified && !m_selectedModel->isModified() ) + m_noOfModified++; + else if ( !modified && m_selectedModel->isModified() ) + m_noOfModified--; + + kdDebug(8101) << "After : m_noOfModified = " << m_noOfModified << endl; + + if ( m_noOfModified < 0 ) + { + kdDebug(8101) << "Wow something is ****ed up..." << endl; + } + else if ( m_noOfModified == 0 ) + { + emit setModified( false ); + } + else // > 0 :-) + { + emit setModified( true ); + } +} + +bool KompareModelList::setSelectedModel( DiffModel* model ) +{ + kdDebug(8101) << "KompareModelList::setSelectedModel( " << model << " )" << endl; + + if ( model != m_selectedModel ) + { + if ( m_models->findIndex( model ) == -1 ) + return false; + kdDebug(8101) << "m_selectedModel (was) = " << m_selectedModel << endl; + m_modelIndex = m_models->findIndex( model ); + kdDebug(8101) << "m_selectedModel (is) = " << m_selectedModel << endl; + m_selectedModel = model; + } + + updateModelListActions(); + + return true; +} + +void KompareModelList::updateModelListActions() +{ + if ( m_models && m_selectedModel && m_selectedDifference ) + { + if ( ( ( KomparePart* )parent() )->isReadWrite() ) + { + if ( m_selectedModel->appliedCount() != m_selectedModel->differenceCount() ) + m_applyAll->setEnabled( true ); + else + m_applyAll->setEnabled( false ); + + if ( m_selectedModel->appliedCount() != 0 ) + m_unapplyAll->setEnabled( true ); + else + m_unapplyAll->setEnabled( false ); + + m_applyDifference->setEnabled( true ); + m_unApplyDifference->setEnabled( true ); + m_save->setEnabled( m_selectedModel->isModified() ); + } + else + { + m_applyDifference->setEnabled ( false ); + m_unApplyDifference->setEnabled( false ); + m_applyAll->setEnabled ( false ); + m_unapplyAll->setEnabled ( false ); + m_save->setEnabled ( false ); + } + + m_previousFile->setEnabled ( hasPrevModel() ); + m_nextFile->setEnabled ( hasNextModel() ); + m_previousDifference->setEnabled( hasPrevDiff() ); + m_nextDifference->setEnabled ( hasNextDiff() ); + } + else + { + m_applyDifference->setEnabled ( false ); + m_unApplyDifference->setEnabled ( false ); + m_applyAll->setEnabled ( false ); + m_unapplyAll->setEnabled ( false ); + + m_previousFile->setEnabled ( false ); + m_nextFile->setEnabled ( false ); + m_previousDifference->setEnabled( false ); + m_nextDifference->setEnabled ( false ); + m_save->setEnabled ( false ); + } +} + +bool KompareModelList::hasPrevModel() const +{ + kdDebug(8101) << "KompareModelList::hasPrevModel()" << endl; + + if ( m_modelIndex > 0 ) + { +// kdDebug(8101) << "has prev model" << endl; + return true; + } + +// kdDebug(8101) << "doesn't have a prev model, this is the first one..." << endl; + + return false; +} + +bool KompareModelList::hasNextModel() const +{ + kdDebug(8101) << "KompareModelList::hasNextModel()" << endl; + + if ( ( unsigned int )m_modelIndex < ( m_models->count() - 1 ) ) + { +// kdDebug(8101) << "has next model" << endl; + return true; + } + +// kdDebug(8101) << "doesn't have a next model, this is the last one..." << endl; + return false; +} + +bool KompareModelList::hasPrevDiff() const +{ +// kdDebug(8101) << "KompareModelList::hasPrevDiff()" << endl; + int index = m_selectedModel->diffIndex(); + + if ( index > 0 ) + { +// kdDebug(8101) << "has prev difference in same model" << endl; + return true; + } + + if ( hasPrevModel() ) + { +// kdDebug(8101) << "has prev difference but in prev model" << endl; + return true; + } + +// kdDebug(8101) << "doesn't have a prev difference, not even in the previous model because there is no previous model" << endl; + + return false; +} + +bool KompareModelList::hasNextDiff() const +{ +// kdDebug(8101) << "KompareModelList::hasNextDiff()" << endl; + int index = m_selectedModel->diffIndex(); + + if ( index < ( m_selectedModel->differenceCount() - 1 ) ) + { +// kdDebug(8101) << "has next difference in same model" << endl; + return true; + } + + if ( hasNextModel() ) + { +// kdDebug(8101) << "has next difference but in next model" << endl; + return true; + } + +// kdDebug(8101) << "doesn't have a next difference, not even in next model because there is no next model" << endl; + + return false; +} + +void KompareModelList::slotActionApplyDifference() +{ + if ( !m_selectedDifference->applied() ) + slotApplyDifference( true ); + slotNextDifference(); + updateModelListActions(); +} + +void KompareModelList::slotActionUnApplyDifference() +{ + if ( m_selectedDifference->applied() ) + slotApplyDifference( false ); + slotPreviousDifference(); + updateModelListActions(); +} + +void KompareModelList::slotActionApplyAllDifferences() +{ + slotApplyAllDifferences( true ); + updateModelListActions(); +} + +void KompareModelList::slotActionUnapplyAllDifferences() +{ + slotApplyAllDifferences( false ); + updateModelListActions(); +} + +#include "komparemodellist.moc" + +/* vim: set ts=4 sw=4 noet: */ diff --git a/kompare/libdiff2/komparemodellist.h b/kompare/libdiff2/komparemodellist.h new file mode 100644 index 00000000..8ba264fb --- /dev/null +++ b/kompare/libdiff2/komparemodellist.h @@ -0,0 +1,213 @@ +/*************************************************************************** + komparemodellist.h - description + ------------------- + begin : Tue Jun 26 2001 + copyright : (C) 2001-2003 by John Firebaugh + and Otto Bruggeman + email : jfirebaugh@kde.org + otto.bruggeman@home.nl + ***************************************************************************/ + +/*************************************************************************** + * * + * This program 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 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef KOMPAREMODELLIST_H +#define KOMPAREMODELLIST_H + +#include <qobject.h> + +#include "diffmodel.h" +#include "diffmodellist.h" +#include "kompare.h" + +class QFile; + +class KAction; +class KDirWatch; +class KTempFile; + +class DiffSettings; +class KompareProcess; + +namespace Diff2 +{ + +class KompareModelList : public QObject +{ + Q_OBJECT +public: + KompareModelList( DiffSettings* diffSettings, struct Kompare::Info& info, QObject* parent = 0, const char* name = 0 ); + ~KompareModelList(); + +public: + // Swap source with destination and show differences + void swap(); + + /* Comparing methods */ + bool compare( const QString& source, const QString& destination ); + + bool compareFiles( const QString& source, const QString& destination ); + bool compareDirs( const QString& source, const QString& destination ); + + bool openDiff( const QString& diff ); + + bool openFileAndDiff( const QString& file, const QString& diff ); + bool openDirAndDiff( const QString& dir, const QString& diff ); + + bool saveDiff( const QString& url, QString directory, DiffSettings* diffSettings ); + bool saveAll(); + + bool saveDestination( DiffModel* model ); + + void setEncoding( const QString& encoding ); + + QString recreateDiff() const; + + // This parses the difflines and creates new models + int parseDiffOutput( const QString& diff ); + + // Call this to emit the signals to the rest of the "world" to show the diff + void show(); + + // This will blend the original URL (dir or file) into the diffmodel, + // this is like patching but with a twist + bool blendOriginalIntoModelList( const QString& localURL ); + + enum Kompare::Mode mode() const { return m_info.mode; }; + const DiffModelList* models() const { return m_models; }; + + int modelCount() const; + int differenceCount() const; + int appliedCount() const; + + const DiffModel* modelAt( int i ) const { return *( m_models->at( i ) ); }; + int findModel( DiffModel* model ) const { return m_models->findIndex( model ); }; + + bool isModified() const; + + int currentModel() const { return m_models->findIndex( m_selectedModel ); }; + int currentDifference() const { return m_selectedModel ? m_selectedModel->findDifference( m_selectedDifference ) : -1; }; + + const DiffModel* selectedModel() const { return m_selectedModel; }; + const Difference* selectedDifference() const { return m_selectedDifference; }; + + void clear(); + +private: + Diff2::DiffModel* firstModel(); + Diff2::DiffModel* lastModel(); + Diff2::DiffModel* prevModel(); + Diff2::DiffModel* nextModel(); + + bool setSelectedModel( Diff2::DiffModel* model ); + + void updateModelListActions(); + +protected: + bool blendFile( DiffModel* model, const QString& lines ); + +signals: + void status( Kompare::Status status ); + void setStatusBarModelInfo( int modelIndex, int differenceIndex, int modelCount, int differenceCount, int appliedCount ); + void error( QString error ); + void modelsChanged( const Diff2::DiffModelList* models ); + void setSelection( const Diff2::DiffModel* model, const Diff2::Difference* diff ); + void setSelection( const Diff2::Difference* diff ); + void applyDifference( bool apply ); + void applyAllDifferences( bool apply ); + void applyDifference( const Diff2::Difference* diff, bool apply ); + + // Emits true when m_noOfModified > 0, false when m_noOfModified == 0 + void setModified( bool modified ); + +public slots: + void slotSelectionChanged( const Diff2::DiffModel* model, const Diff2::Difference* diff ); + void slotSelectionChanged( const Diff2::Difference* diff ); + + void slotApplyDifference( bool apply ); + void slotApplyAllDifferences( bool apply ); + void slotPreviousModel(); + void slotNextModel(); + void slotPreviousDifference(); + void slotNextDifference(); + + // This slot is called by the diffmodels whenever their status changes to modified or unmodified + void slotSetModified( bool modified ); + +protected slots: + void slotDiffProcessFinished( bool success ); + void slotWriteDiffOutput( bool success ); + + void slotActionApplyDifference(); + void slotActionUnApplyDifference(); + void slotActionApplyAllDifferences(); + void slotActionUnapplyAllDifferences(); + + /** Save the currently selected destination in a multi-file diff, + or the single destination if a single file diff. */ + void slotSaveDestination(); + +private slots: + void slotDirectoryChanged( const QString& ); + void slotFileChanged( const QString& ); + +private: // Helper methods + bool isDirectory( const QString& url ) const; + bool isDiff( const QString& mimetype ) const; + QString readFile( const QString& fileName ); + + bool hasPrevModel() const; + bool hasNextModel() const; + bool hasPrevDiff() const; + bool hasNextDiff() const; + + QStringList split( const QString& diff ); + +private: + KTempFile* m_diffTemp; + QString m_diffURL; + + KompareProcess* m_diffProcess; + + DiffSettings* m_diffSettings; + + DiffModelList* m_models; + + QString m_source; + QString m_destination; + + DiffModel* m_selectedModel; + Difference* m_selectedDifference; + + KDirWatch* m_dirWatch; + KDirWatch* m_fileWatch; + + int m_noOfModified; + unsigned int m_modelIndex; + + struct Kompare::Info& m_info; + + KAction* m_applyDifference; + KAction* m_unApplyDifference; + KAction* m_applyAll; + KAction* m_unapplyAll; + KAction* m_previousFile; + KAction* m_nextFile; + KAction* m_previousDifference; + KAction* m_nextDifference; + + KAction* m_save; + + QString m_encoding; + QTextCodec* m_textCodec; +}; + +} // End of namespace Diff2 + +#endif diff --git a/kompare/libdiff2/kompareprocess.cpp b/kompare/libdiff2/kompareprocess.cpp new file mode 100644 index 00000000..2d5eac00 --- /dev/null +++ b/kompare/libdiff2/kompareprocess.cpp @@ -0,0 +1,269 @@ +/*************************************************************************** + kompareprocess.cpp - description + ------------------- + begin : Sun Mar 4 2001 + copyright : (C) 2001-2003 by Otto Bruggeman + and John Firebaugh + (C) 2007 Kevin Kofler + email : otto.bruggeman@home.nl + jfirebaugh@kde.org + kevin.kofler@chello.at +****************************************************************************/ + +/*************************************************************************** +** +** This program 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 2 of the License, or +** (at your option) any later version. +** +***************************************************************************/ + +#include <qdir.h> +#include <qstringlist.h> +#include <qtextcodec.h> + +#include <kcharsets.h> +#include <kdebug.h> +#include <kglobal.h> + +#include "diffsettings.h" +#include "kompareprocess.h" + +KompareProcess::KompareProcess( DiffSettings* diffSettings, enum Kompare::DiffMode mode, QString source, QString destination, QString dir ) + : KProcess(), + m_diffSettings( diffSettings ), + m_mode( mode ), + m_textDecoder( 0 ) +{ + setUseShell( true ); + + // connect the stdout and stderr signals + connect( this, SIGNAL( receivedStdout( KProcess*, char*, int ) ), + SLOT ( slotReceivedStdout( KProcess*, char*, int ) ) ); + connect( this, SIGNAL( receivedStderr( KProcess*, char*, int ) ), + SLOT ( slotReceivedStderr( KProcess*, char*, int ) ) ); + + // connect the signal that indicates that the proces has exited + connect( this, SIGNAL( processExited( KProcess* ) ), + SLOT ( slotProcessExited( KProcess* ) ) ); + + *this << "LANG=C"; + + // Write command and options + if( m_mode == Kompare::Default ) + { + writeDefaultCommandLine(); + } + else + { + writeCommandLine(); + } + + if( !dir.isEmpty() ) { + QDir::setCurrent( dir ); + } + + // Write file names + *this << "--"; + *this << KProcess::quote( constructRelativePath( dir, source ) ); + *this << KProcess::quote( constructRelativePath( dir, destination ) ); +} + +void KompareProcess::writeDefaultCommandLine() +{ + if ( !m_diffSettings || m_diffSettings->m_diffProgram.isEmpty() ) + { + *this << "diff" << "-dr"; + } + else + { + *this << m_diffSettings->m_diffProgram << "-dr"; + } + + *this << "-U" << QString::number( m_diffSettings->m_linesOfContext ); +} + +void KompareProcess::writeCommandLine() +{ + // load the executable into the KProcess + if ( m_diffSettings->m_diffProgram.isEmpty() ) + { + kdDebug(8101) << "Using the first diff in the path..." << endl; + *this << "diff"; + } + else + { + kdDebug(8101) << "Using a user specified diff, namely: " << m_diffSettings->m_diffProgram << endl; + *this << m_diffSettings->m_diffProgram; + } + + switch( m_diffSettings->m_format ) { + case Kompare::Unified : + *this << "-U" << QString::number( m_diffSettings->m_linesOfContext ); + break; + case Kompare::Context : + *this << "-C" << QString::number( m_diffSettings->m_linesOfContext ); + break; + case Kompare::RCS : + *this << "-n"; + break; + case Kompare::Ed : + *this << "-e"; + break; + case Kompare::SideBySide: + *this << "-y"; + break; + case Kompare::Normal : + case Kompare::UnknownFormat : + default: + break; + } + + if ( m_diffSettings->m_largeFiles ) + { + *this << "-H"; + } + + if ( m_diffSettings->m_ignoreWhiteSpace ) + { + *this << "-b"; + } + + if ( m_diffSettings->m_ignoreAllWhiteSpace ) + { + *this << "-w"; + } + + if ( m_diffSettings->m_ignoreEmptyLines ) + { + *this << "-B"; + } + + if ( m_diffSettings->m_ignoreChangesDueToTabExpansion ) + { + *this << "-E"; + } + + if ( m_diffSettings->m_createSmallerDiff ) + { + *this << "-d"; + } + + if ( m_diffSettings->m_ignoreChangesInCase ) + { + *this << "-i"; + } + + if ( m_diffSettings->m_ignoreRegExp && !m_diffSettings->m_ignoreRegExpText.isEmpty() ) + { + *this << "-I " << KProcess::quote( m_diffSettings->m_ignoreRegExpText ); + } + + if ( m_diffSettings->m_showCFunctionChange ) + { + *this << "-p"; + } + + if ( m_diffSettings->m_convertTabsToSpaces ) + { + *this << "-t"; + } + + if ( m_diffSettings->m_recursive ) + { + *this << "-r"; + } + + if ( m_diffSettings->m_newFiles ) + { + *this << "-N"; + } + +// This option is more trouble than it is worth... please do not ever enable it unless you want really weird crashes +// if ( m_diffSettings->m_allText ) +// { +// *this << "-a"; +// } + + if ( m_diffSettings->m_excludeFilePattern ) + { + QStringList::ConstIterator it = m_diffSettings->m_excludeFilePatternList.begin(); + QStringList::ConstIterator end = m_diffSettings->m_excludeFilePatternList.end(); + for ( ; it != end; ++it ) + { + *this << "-x" << KProcess::quote( *it ); + } + } + + if ( m_diffSettings->m_excludeFilesFile && !m_diffSettings->m_excludeFilesFileURL.isEmpty() ) + { + *this << "-X" << KProcess::quote( m_diffSettings->m_excludeFilesFileURL ); + } +} + +KompareProcess::~KompareProcess() +{ +} + +void KompareProcess::setEncoding( const QString& encoding ) +{ + if ( encoding.lower() == "default" ) + { + m_textDecoder = QTextCodec::codecForLocale()->makeDecoder(); + } + else + { + QTextCodec* textCodec = KGlobal::charsets()->codecForName( encoding.latin1() ); + if ( textCodec ) + m_textDecoder = textCodec->makeDecoder(); + else + { + kdDebug(8101) << "Using locale codec as backup..." << endl; + textCodec = QTextCodec::codecForLocale(); + m_textDecoder = textCodec->makeDecoder(); + } + } +} + +void KompareProcess::slotReceivedStdout( KProcess* /* process */, char* buffer, int length ) +{ + // add all output to m_stdout + if ( m_textDecoder ) + m_stdout += m_textDecoder->toUnicode( buffer, length ); + else + kdDebug(8101) << "KompareProcess::slotReceivedStdout : No decoder !!!" << endl; +} + +void KompareProcess::slotReceivedStderr( KProcess* /* process */, char* buffer, int length ) +{ + // add all output to m_stderr + if ( m_textDecoder ) + m_stderr += m_textDecoder->toUnicode( buffer, length ); + else + kdDebug(8101) << "KompareProcess::slotReceivedStderr : No decoder !!!" << endl; +} + +bool KompareProcess::start() +{ +#ifndef NDEBUG + QString cmdLine; + QValueList<QCString>::ConstIterator it = arguments.begin(); + for (; it != arguments.end(); ++it ) + cmdLine += "\"" + (*it) + "\" "; + kdDebug(8101) << cmdLine << endl; +#endif + return( KProcess::start( KProcess::NotifyOnExit, KProcess::AllOutput ) ); +} + +void KompareProcess::slotProcessExited( KProcess* /* proc */ ) +{ + // exit status of 0: no differences + // 1: some differences + // 2: error but there may be differences ! + kdDebug(8101) << "Exited with exit status : " << exitStatus() << endl; + emit diffHasFinished( normalExit() && exitStatus() != 0 ); +} + +#include "kompareprocess.moc" + diff --git a/kompare/libdiff2/kompareprocess.h b/kompare/libdiff2/kompareprocess.h new file mode 100644 index 00000000..06a4a5ce --- /dev/null +++ b/kompare/libdiff2/kompareprocess.h @@ -0,0 +1,67 @@ +/*************************************************************************** + kompareprocess.h - description + ------------------- + begin : Sun Mar 4 2001 + copyright : (C) 2001-2003 by Otto Bruggeman + and John Firebaugh + email : otto.bruggeman@home.nl + jfirebaugh@kde.org +****************************************************************************/ + +/*************************************************************************** +** +** This program 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 2 of the License, or +** (at your option) any later version. +** +***************************************************************************/ + +#ifndef KOMPAREPROCESS_H +#define KOMPAREPROCESS_H + +#include <kprocess.h> + +#include "kompare.h" + +class QTextCodec; + +class DiffSettings; + +class KompareProcess : public KProcess, public KompareFunctions +{ + Q_OBJECT + +public: + KompareProcess( DiffSettings* diffSettings, enum Kompare::DiffMode mode, QString source, QString destination, QString directory = QString::null ); + ~KompareProcess(); + + bool start(); + + QString diffOutput() { return m_stdout; } + QString stdOut() { return m_stdout; } + QString stdErr() { return m_stderr; } + + void setEncoding( const QString& encoding ); + +signals: + void diffHasFinished( bool finishedNormally ); + +protected: + void writeDefaultCommandLine(); + void writeCommandLine(); + +protected slots: + void slotReceivedStdout( KProcess*, char*, int ); + void slotReceivedStderr( KProcess*, char*, int ); + void slotProcessExited( KProcess* proc ); + +private: + DiffSettings* m_diffSettings; + enum Kompare::DiffMode m_mode; + QString m_stdout; + QString m_stderr; + QTextDecoder* m_textDecoder; +}; + +#endif diff --git a/kompare/libdiff2/levenshteintable.cpp b/kompare/libdiff2/levenshteintable.cpp new file mode 100644 index 00000000..54525aef --- /dev/null +++ b/kompare/libdiff2/levenshteintable.cpp @@ -0,0 +1,332 @@ +/******************************************************************************* +** +** Filename : levenshteintable.cpp +** Created on : 08 november, 2003 +** Copyright : (c) 2003 Otto Bruggeman +** Email : bruggie@home.nl +** +*******************************************************************************/ + +/******************************************************************************* +** +** This program 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 2 of the License, or +** (at your option) any later version. +** +*******************************************************************************/ + +#include <iostream> + +#include <qstring.h> + +#include <kdebug.h> +#include <kglobal.h> + +#include "levenshteintable.h" + +#include "difference.h" + +using namespace Diff2; + +LevenshteinTable::LevenshteinTable() + : m_width( 256 ), + m_height( 256 ), + m_size( m_height * m_width ), + m_table( new unsigned int[ m_size ] ), + m_source( 0 ), + m_destination( 0 ) +{ +} + +LevenshteinTable::LevenshteinTable( unsigned int width, unsigned int height ) + : m_width( width ), + m_height( height ), + m_size( m_width * m_height ), + m_table( new unsigned int[ m_size ] ), + m_source( 0 ), + m_destination( 0 ) +{ +} + +LevenshteinTable::~LevenshteinTable() +{ + delete[] m_table; + m_source = 0; + m_destination = 0; +} + +int LevenshteinTable::getContent( unsigned int posX, unsigned int posY ) const +{ +// kdDebug(8101) << "Width = " << m_width << ", height = " << m_height << ", posX = " << posX << ", posY = " << posY << endl; + return m_table[ posY * m_width + posX ]; +} + +int LevenshteinTable::setContent( unsigned int posX, unsigned int posY, int value ) +{ + m_table[ posY * m_width + posX ] = value; + + return 0; +} + +bool LevenshteinTable::setSize( unsigned int width, unsigned int height ) +{ + // Set a limit of 16.7 million entries, will be about 64 MB of ram, that should be plenty + if ( ( ( width ) * ( height ) ) > ( 256 * 256 * 256 ) ) + return false; + + if ( ( ( width ) * ( height ) ) > m_size ) + { + delete[] m_table; + + m_size = width * height; + m_table = new unsigned int[ m_size ]; + } + + m_width = width; + m_height = height; + + return true; +} + +void LevenshteinTable::dumpLevenshteinTable() +{ + for ( unsigned int i = 0; i < m_height; ++i ) + { + for ( unsigned int j = 0; j < m_width; ++j ) + { + std::cout.width( 3 ); + std::cout << getContent( j, i ); + } + std::cout << std::endl; + } +} + +unsigned int LevenshteinTable::createTable( DifferenceString* source, DifferenceString* destination ) +{ + m_source = source; + m_destination = destination; + + QString s = ' ' + source->string(); // Optimization, so i dont have to subtract 1 from the indexes every + QString d = ' ' + destination->string(); // single time and add 1 to the width and height of the table + + unsigned int m = s.length(); + unsigned int n = d.length(); + + const QChar* sq = s.unicode(); + const QChar* dq = d.unicode(); + + if ( m == 1 ) + return --n; + + if ( n == 1 ) + return --m; + + if ( !setSize( m, n ) ) + return 0; + + unsigned int i; + unsigned int j; + + // initialize first row + for ( i = 0; i < m; ++i ) + setContent( i, 0, i ); + // initialize first column + for ( j = 0; j < n; ++j ) + setContent( 0, j, j ); + + int cost = 0, north = 0, west = 0, northwest = 0; + + ushort si, dj; + // Optimization, calculate row wise instead of column wise, wont trash the cache so much with large strings + for ( j = 1; j < n; ++j ) + { + dj = dq[ j ]; + + for ( i = 1; i < m; ++i ) + { + si = sq[ i ]; + if ( si == dj ) + cost = 0; + else + cost = 1; + + north = getContent( i, j-1 ) + 1; + west = getContent( i-1, j ) + 1; + northwest = getContent( i-1, j-1 ) + cost; + + setContent( i, j, kMin( north, kMin( west, northwest ) ) ); + } + } + + return getContent( m-1, n-1 ); +} + +int LevenshteinTable::chooseRoute( int c1, int c2, int c3 ) +{ +// kdDebug(8101) << "c1 = " << c1 << ", c2 = " << c2 << ", c3 = " << c3 << endl; + // preference order: c2, c3, c1, hopefully this will work out for me + if ( c2 <= c1 && c2 <= c3 ) + return 1; + + if ( c3 <= c2 && c3 <= c1 ) + return 2; + + return 0; +} + +void LevenshteinTable::createListsOfMarkers() +{ +// std::cout << source.latin1() << std::endl; +// std::cout << destination.latin1() << std::endl; +// dumpLevenshteinTable(); + + unsigned int x = m_width-1; + unsigned int y = m_height-1; + + Marker* c = 0; + + int n, nw, w, direction, currentValue; + while ( x > 0 && y > 0 ) + { + currentValue = getContent( x, y ); + + nw = getContent( x - 1, y - 1 ); + n = getContent( x, y - 1 ); + w = getContent( x - 1, y ); + + direction = chooseRoute( n, nw, w ); + + switch ( direction ) + { + case 0: // north +// kdDebug(8101) << "Picking north" << endl; +// kdDebug(8101) << "Source[" << ( x - 1 ) << "] = " << QString( source[ x-1 ] ) << ", destination[" << ( y - 1 ) << "] = " << QString( destination[ y-1 ] ) << endl; + + if ( !m_destination->markerList().isEmpty() ) + c = m_destination->markerList().first(); + else + c = 0; + + if ( c && c->type() == Marker::End ) + { +// kdDebug(8101) << "CurrentValue: " << currentValue << endl; + if ( n == currentValue ) + m_destination->prepend( new Marker( Marker::Start, y ) ); + // else: the change continues, dont do anything + } + else + { +// kdDebug(8101) << "CurrentValue: " << currentValue << endl; + if ( n < currentValue ) + m_destination->prepend( new Marker( Marker::End, y ) ); + } + + --y; + break; + case 1: // northwest +// kdDebug(8101) << "Picking northwest" << endl; +// kdDebug(8101) << "Source[" << ( x - 1 ) << "] = " << QString( source[ x-1 ] ) << ", destination[" << ( y - 1 ) << "] = " << QString( destination[ y-1 ] ) << endl; + + if ( !m_destination->markerList().isEmpty() ) + c = m_destination->markerList().first(); + else + c = 0; + + if ( c && c->type() == Marker::End ) + { +// kdDebug(8101) << "End found: CurrentValue: " << currentValue << endl; + if ( nw == currentValue ) + m_destination->prepend( new Marker( Marker::Start, y ) ); + // else: the change continues, dont do anything + } + else + { +// kdDebug(8101) << "CurrentValue: " << currentValue << endl; + if ( nw < currentValue ) + m_destination->prepend( new Marker( Marker::End, y ) ); + } + + if ( !m_source->markerList().isEmpty() ) + c = m_source->markerList().first(); + else + c = 0; + + if ( c && c->type() == Marker::End ) + { +// kdDebug(8101) << "End found: CurrentValue: " << currentValue << endl; + if ( nw == currentValue ) + m_source->prepend( new Marker( Marker::Start, x ) ); + // else: the change continues, dont do anything + } + else + { +// kdDebug(8101) << "CurrentValue: " << currentValue << endl; + if ( nw < currentValue ) + m_source->prepend( new Marker( Marker::End, x ) ); + } + + --y; + --x; + break; + case 2: // west +// kdDebug(8101) << "Picking west" << endl; +// kdDebug(8101) << "Source[" << ( x - 1 ) << "] = " << QString( source[ x-1 ] ) << ", destination[" << ( y - 1 ) << "] = " << QString( destination[ y-1 ] ) << endl; + + if ( !m_source->markerList().isEmpty() ) + c = m_source->markerList().first(); + else + c = 0; + + if ( c && c->type() == Marker::End ) + { +// kdDebug(8101) << "End found: CurrentValue: " << currentValue << endl; + if ( w == currentValue ) + m_source->prepend( new Marker( Marker::Start, x ) ); + // else: the change continues, dont do anything + } + else + { +// kdDebug(8101) << "CurrentValue: " << currentValue << endl; + if ( w < currentValue ) + m_source->prepend( new Marker( Marker::End, x ) ); + } + + --x; + break; + } + } + +// kdDebug(8101) << "Source string: " << m_source->string() << endl; +// c = m_source->markerList()->first(); +// QStringList list; +// unsigned int prevValue = 0; +// for ( ; c; c = m_source->markerList()->next() ) +// { +// kdDebug(8101) << "Source Marker Entry : Type: " << c->type() << ", Offset: " << c->offset() << endl; +// list.append( m_source->string().mid( prevValue, c->offset() - prevValue ) ); +// prevValue = c->offset(); +// } +// if ( prevValue < m_source->string().length() - 1 ) +// { +// list.append( m_source->string().mid( prevValue, m_source->string().length() - prevValue ) ); +// } +// kdDebug(8101) << "Source Resulting stringlist : " << list.join("\n") << endl; + +// list.clear(); +// prevValue = 0; + +// kdDebug(8101) << "Destination string: " << m_destination->string() << endl; +// for ( ; c; c = m_destination->markerList()->next() ) +// { +// kdDebug(8101) << "Destination Marker Entry : Type: " << c->type() << ", Offset: " << c->offset() << endl; +// list.append( m_destination->string().mid( prevValue, c->offset() - prevValue ) ); +// prevValue = c->offset(); +// } +// if ( prevValue < m_destination->string().length() - 1 ) +// { +// list.append( m_destination->string().mid( prevValue, m_destination->string().length() - prevValue ) ); +// } +// kdDebug(8101) << "Destination Resulting string : " << list.join("\n") << endl; +} + diff --git a/kompare/libdiff2/levenshteintable.h b/kompare/libdiff2/levenshteintable.h new file mode 100644 index 00000000..201d1c10 --- /dev/null +++ b/kompare/libdiff2/levenshteintable.h @@ -0,0 +1,69 @@ +/******************************************************************************* +** +** Filename : levenshteintable.h +** Created on : 08 november, 2003 +** Copyright : (c) 2003 Otto Bruggeman +** Email : bruggie@home.nl +** +*******************************************************************************/ + +/******************************************************************************* +** +** This program 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 2 of the License, or +** (at your option) any later version. +** +*******************************************************************************/ + +#ifndef _LEVENSHTEIN_H +#define _LEVENSHTEIN_H + +#include "difference.h" + +class QString; + +namespace Diff2 { + +class Marker; + +class LevenshteinTable +{ +public: + LevenshteinTable(); + LevenshteinTable( unsigned int width, unsigned int height ); + ~LevenshteinTable(); + +public: + int getContent( unsigned int posX, unsigned int posY ) const; + int setContent( unsigned int posX, unsigned int posY, int value ); + bool setSize ( unsigned int width, unsigned int height ); + + unsigned int width() const { return m_width; }; + unsigned int height() const { return m_height; }; + + /** Debug method to check if the table is properly filled */ + void dumpLevenshteinTable( void ); + + /** This will calculate the levenshtein distance of 2 strings */ + unsigned int createTable( DifferenceString* s, DifferenceString* d ); + + void createListsOfMarkers( void ); + int chooseRoute( int c1, int c2, int c3 ); + +protected: + LevenshteinTable( const LevenshteinTable& table ); + const LevenshteinTable& operator = ( const LevenshteinTable& table ); + +private: + unsigned int m_width; + unsigned int m_height; + unsigned int m_size; + unsigned int* m_table; + DifferenceString* m_source; + DifferenceString* m_destination; +}; + +} // namespace Diff2 + +#endif // _LEVENSHTEIN_H diff --git a/kompare/libdiff2/parser.cpp b/kompare/libdiff2/parser.cpp new file mode 100644 index 00000000..04ff7a4a --- /dev/null +++ b/kompare/libdiff2/parser.cpp @@ -0,0 +1,139 @@ +/************************************************************************** +** parser.cpp +** ------------------- +** begin : Sun Aug 4 15:05:35 2002 +** copyright : (C) 2002-2004 Otto Bruggeman +** email : otto.bruggeman@home.nl +** +***************************************************************************/ +/*************************************************************************** +** +** This program 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 2 of the License, or +** ( at your option ) any later version. +** +***************************************************************************/ + +#include <kdebug.h> + +#include "parser.h" +#include "cvsdiffparser.h" +#include "diffparser.h" +#include "perforceparser.h" +#include "diffmodel.h" + +using namespace Diff2; + +Parser::Parser( const KompareModelList* list ) : + m_list( list ) +{ +} + +Parser::~Parser() +{ +} + +int Parser::cleanUpCrap( QStringList& diffLines ) +{ + QStringList::Iterator it = diffLines.begin(); + + int nol = 0; + + QString noNewLine( "\\ No newline" ); + + for ( ; it != diffLines.end(); ++it ) + { + if ( (*it).startsWith( noNewLine ) ) + { + it = diffLines.remove( it ); + // correcting the advance of the iterator because of the remove + --it; + QString temp( *it ); + temp.truncate( temp.find( '\n' ) ); + *it = temp; + ++nol; + } + } + + return nol; +} + +DiffModelList* Parser::parse( QStringList& diffLines ) +{ + /* Basically determine the generator then call the parse method */ + ParserBase* parser; + + m_generator = determineGenerator( diffLines ); + + int nol = cleanUpCrap( diffLines ); + kdDebug(8101) << "Cleaned up " << nol << " line(s) of crap from the diff..." << endl; + + switch( m_generator ) + { + case Kompare::CVSDiff : + kdDebug(8101) << "It is a CVS generated diff..." << endl; + parser = new CVSDiffParser( m_list, diffLines ); + break; + case Kompare::Diff : + kdDebug(8101) << "It is a diff generated diff..." << endl; + parser = new DiffParser( m_list, diffLines ); + break; + case Kompare::Perforce : + kdDebug(8101) << "It is a Perforce generated diff..." << endl; + parser = new PerforceParser( m_list, diffLines ); + break; + default: + // Nothing to delete, just leave... + return 0L; + } + + m_format = parser->format(); + DiffModelList* modelList = parser->parse(); + if ( modelList ) + { + kdDebug(8101) << "Modelcount: " << modelList->count() << endl; + DiffModelListIterator modelIt = modelList->begin(); + DiffModelListIterator mEnd = modelList->end(); + for ( ; modelIt != mEnd; ++modelIt ) + { + kdDebug(8101) << "Hunkcount: " << (*modelIt)->hunkCount() << endl; + kdDebug(8101) << "Diffcount: " << (*modelIt)->differenceCount() << endl; + } + } + + delete parser; + + return modelList; +} + +enum Kompare::Generator Parser::determineGenerator( const QStringList& diffLines ) +{ + // Shit have to duplicate some code with this method and the ParserBase derived classes + QString cvsDiff ( "Index: " ); + QString perforceDiff( "==== " ); + + QStringList::ConstIterator it = diffLines.begin(); + QStringList::ConstIterator linesEnd = diffLines.end(); + + while ( it != linesEnd ) + { + if ( ( *it ).startsWith( cvsDiff ) ) + { + kdDebug(8101) << "Diff is a CVSDiff" << endl; + return Kompare::CVSDiff; + } + else if ( ( *it ).startsWith( perforceDiff ) ) + { + kdDebug(8101) << "Diff is a Perforce Diff" << endl; + return Kompare::Perforce; + } + ++it; + } + + kdDebug(8101) << "We'll assume it is a diff Diff" << endl; + + // For now we'll assume it is a diff file diff, later we might + // try to really determine if it is a diff file diff. + return Kompare::Diff; +} diff --git a/kompare/libdiff2/parser.h b/kompare/libdiff2/parser.h new file mode 100644 index 00000000..0ffae23a --- /dev/null +++ b/kompare/libdiff2/parser.h @@ -0,0 +1,58 @@ +/************************************************************************** +** parser.h +** -------- +** begin : Tue Jul 30 23:53:52 2002 +** copyright : (C) 2002-2004 Otto Bruggeman +** email : otto.bruggeman@home.nl +** +***************************************************************************/ +/*************************************************************************** +** +** This program 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 2 of the License, or +** ( at your option ) any later version. +** +***************************************************************************/ + +#ifndef _DIFF2_PARSER_H +#define _DIFF2_PARSER_H + +#include "diffmodellist.h" +#include "kompare.h" + +namespace Diff2 +{ + +class DiffModel; +class KompareModelList; + +class Parser +{ +public: + Parser( const KompareModelList* list ); + ~Parser(); + +public: + DiffModelList* parse( QStringList& diffLines ); + + enum Kompare::Generator generator() const { return m_generator; }; + enum Kompare::Format format() const { return m_format; }; + +private: + /** Which program was used to generate the output */ + enum Kompare::Generator determineGenerator( const QStringList& diffLines ); + + int cleanUpCrap( QStringList& diffLines ); + +private: + enum Kompare::Generator m_generator; + enum Kompare::Format m_format; + + const KompareModelList* m_list; +}; + +} // End of namespace Diff2 + +#endif + diff --git a/kompare/libdiff2/parserbase.cpp b/kompare/libdiff2/parserbase.cpp new file mode 100644 index 00000000..303f7b22 --- /dev/null +++ b/kompare/libdiff2/parserbase.cpp @@ -0,0 +1,739 @@ +/************************************************************************** +** parserbase.cpp +** ------------------- +** begin : Sun Aug 4 15:05:35 2002 +** copyright : (C) 2002-2004 Otto Bruggeman +** email : otto.bruggeman@home.nl +** +***************************************************************************/ +/*************************************************************************** +** +** This program 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 2 of the License, or +** ( at your option ) any later version. +** +***************************************************************************/ + +#include <qobject.h> + +#include <kdebug.h> + +#include "diffmodel.h" +#include "diffhunk.h" +#include "difference.h" +#include "komparemodellist.h" + +#include "parserbase.h" + +using namespace Diff2; + +ParserBase::ParserBase( const KompareModelList* list, const QStringList& diff ) : + m_diffLines( diff ), + m_currentModel( 0 ), + m_models( 0 ), + m_diffIterator( m_diffLines.begin() ), + m_singleFileDiff( false ), + m_list( list ) +{ +// kdDebug(8101) << diff << endl; +// kdDebug(8101) << m_diffLines << endl; + m_models = new DiffModelList(); + + // used in contexthunkheader + m_contextHunkHeader1.setPattern( "\\*{15} ?(.*)\\n" ); // capture is for function name + m_contextHunkHeader2.setPattern( "\\*\\*\\* ([0-9]+),([0-9]+) \\*\\*\\*\\*\\n" ); + // used in contexthunkbody + m_contextHunkHeader3.setPattern( "--- ([0-9]+),([0-9]+) ----\\n" ); + + m_contextHunkBodyRemoved.setPattern( "- (.*)" ); + m_contextHunkBodyAdded.setPattern ( "\\+ (.*)" ); + m_contextHunkBodyChanged.setPattern( "! (.*)" ); + m_contextHunkBodyContext.setPattern( " (.*)" ); + m_contextHunkBodyLine.setPattern ( "[-\\+! ] (.*)" ); + + // This regexp sucks... i'll see what happens + m_normalDiffHeader.setPattern( "diff (?:(?:-|--)[a-zA-Z0-9=\\\"]+ )*(?:|-- +)(.*) +(.*)\\n" ); + + m_normalHunkHeaderAdded.setPattern ( "([0-9]+)a([0-9]+)(|,[0-9]+)(.*)\\n" ); + m_normalHunkHeaderRemoved.setPattern( "([0-9]+)(|,[0-9]+)d([0-9]+)(.*)\\n" ); + m_normalHunkHeaderChanged.setPattern( "([0-9]+)(|,[0-9]+)c([0-9]+)(|,[0-9]+)(.*)\\n" ); + + m_normalHunkBodyRemoved.setPattern ( "< (.*)" ); + m_normalHunkBodyAdded.setPattern ( "> (.*)" ); + m_normalHunkBodyDivider.setPattern ( "---" ); + + m_unifiedDiffHeader1.setPattern ( "--- ([^\\t]+)(?:\\t([^\\t]+)(?:\\t?)(.*))?\\n" ); + m_unifiedDiffHeader2.setPattern ( "\\+\\+\\+ ([^\\t]+)(?:\\t([^\\t]+)(?:\\t?)(.*))?\\n" ); + m_unifiedHunkHeader.setPattern ( "@@ -([0-9]+)(|,([0-9]+)) \\+([0-9]+)(|,([0-9]+)) @@(?: ?)(.*)\\n" ); + m_unifiedHunkBodyAdded.setPattern ( "\\+(.*)" ); + m_unifiedHunkBodyRemoved.setPattern( "-(.*)" ); + m_unifiedHunkBodyContext.setPattern( " (.*)" ); + m_unifiedHunkBodyLine.setPattern ( "([-+ ])(.*)" ); +} + +ParserBase::~ParserBase() +{ + if ( m_models ) + m_models = 0; // dont delete this, i pass it around... +} + +enum Kompare::Format ParserBase::determineFormat() +{ + // Write your own format detection routine damn it :) + return Kompare::UnknownFormat; +} + +DiffModelList* ParserBase::parse() +{ + switch( determineFormat() ) + { + case Kompare::Context : + return parseContext(); + case Kompare::Ed : + return parseEd(); + case Kompare::Normal : + return parseNormal(); + case Kompare::RCS : + return parseRCS(); + case Kompare::Unified : + return parseUnified(); + default: // Unknown and SideBySide for now + return 0L; + } +} + +bool ParserBase::parseContextDiffHeader() +{ +// kdDebug(8101) << "ParserBase::parseContextDiffHeader()" << endl; + bool result = false; + + while ( m_diffIterator != m_diffLines.end() ) + { + if ( !m_contextDiffHeader1.exactMatch( *(m_diffIterator)++ ) ) + { + continue; + } +// kdDebug(8101) << "Matched length Header1 = " << m_contextDiffHeader1.matchedLength() << endl; +// kdDebug(8101) << "Matched string Header1 = " << m_contextDiffHeader1.cap( 0 ) << endl; + if ( m_diffIterator != m_diffLines.end() && m_contextDiffHeader2.exactMatch( *m_diffIterator ) ) + { +// kdDebug(8101) << "Matched length Header2 = " << m_contextDiffHeader2.matchedLength() << endl; +// kdDebug(8101) << "Matched string Header2 = " << m_contextDiffHeader2.cap( 0 ) << endl; + + m_currentModel = new DiffModel( m_contextDiffHeader1.cap( 1 ), m_contextDiffHeader2.cap( 1 ) ); + QObject::connect( m_currentModel, SIGNAL( setModified( bool ) ), m_list, SLOT( slotSetModified( bool ) ) ); + m_currentModel->setSourceTimestamp ( m_contextDiffHeader1.cap( 2 ) ); + m_currentModel->setSourceRevision ( m_contextDiffHeader1.cap( 4 ) ); + m_currentModel->setDestinationTimestamp( m_contextDiffHeader2.cap( 2 ) ); + m_currentModel->setDestinationRevision ( m_contextDiffHeader2.cap( 4 ) ); + + ++m_diffIterator; + result = true; + + break; + } + else + { + // We're screwed, second line does not match or is not there... + break; + } + // Dont inc the Iterator because the second line might be the first line of + // the context header and the first hit was a fluke (impossible imo) + // maybe we should return false here because the diff is broken ? + } + + return result; +} + +bool ParserBase::parseEdDiffHeader() +{ + return false; +} + +bool ParserBase::parseNormalDiffHeader() +{ +// kdDebug(8101) << "ParserBase::parseNormalDiffHeader()" << endl; + bool result = false; + + while ( m_diffIterator != m_diffLines.end() ) + { + if ( m_normalDiffHeader.exactMatch( *m_diffIterator ) ) + { +// kdDebug(8101) << "Matched length Header = " << m_normalDiffHeader.matchedLength() << endl; +// kdDebug(8101) << "Matched string Header = " << m_normalDiffHeader.cap( 0 ) << endl; + + m_currentModel = new DiffModel(); + QObject::connect( m_currentModel, SIGNAL( setModified( bool ) ), m_list, SLOT( slotSetModified( bool ) ) ); + m_currentModel->setSourceFile ( m_normalDiffHeader.cap( 1 ) ); + m_currentModel->setDestinationFile ( m_normalDiffHeader.cap( 2 ) ); + + result = true; + + ++m_diffIterator; + break; + } + else + { + kdDebug(8101) << "No match for: " << ( *m_diffIterator ) << endl; + } + ++m_diffIterator; + } + + if ( result == false ) + { + // Set this to the first line again and hope it is a single file diff + m_diffIterator = m_diffLines.begin(); + m_currentModel = new DiffModel(); + QObject::connect( m_currentModel, SIGNAL( setModified( bool ) ), m_list, SLOT( slotSetModified( bool ) ) ); + m_singleFileDiff = true; + } + + return result; +} + +bool ParserBase::parseRCSDiffHeader() +{ + return false; +} + +bool ParserBase::parseUnifiedDiffHeader() +{ +// kdDebug(8101) << "ParserBase::parseUnifiedDiffHeader()" << endl; + bool result = false; + + while ( m_diffIterator != m_diffLines.end() ) // dont assume we start with the diffheader1 line + { + if ( !m_unifiedDiffHeader1.exactMatch( *m_diffIterator ) ) + { + ++m_diffIterator; + continue; + } +// kdDebug(8101) << "Matched length Header1 = " << m_unifiedDiffHeader1.matchedLength() << endl; +// kdDebug(8101) << "Matched string Header1 = " << m_unifiedDiffHeader1.cap( 0 ) << endl; + ++m_diffIterator; + if ( m_diffIterator != m_diffLines.end() && m_unifiedDiffHeader2.exactMatch( *m_diffIterator ) ) + { + m_currentModel = new DiffModel( m_unifiedDiffHeader1.cap( 1 ), m_unifiedDiffHeader2.cap( 1 ) ); + QObject::connect( m_currentModel, SIGNAL( setModified( bool ) ), m_list, SLOT( slotSetModified( bool ) ) ); + m_currentModel->setSourceTimestamp( m_unifiedDiffHeader1.cap( 2 ) ); + m_currentModel->setSourceRevision( m_unifiedDiffHeader1.cap( 4 ) ); + m_currentModel->setDestinationTimestamp( m_unifiedDiffHeader2.cap( 2 ) ); + m_currentModel->setDestinationRevision( m_unifiedDiffHeader2.cap( 4 ) ); + + ++m_diffIterator; + result = true; + + break; + } + else + { + // We're screwed, second line does not match or is not there... + break; + } + } + + return result; +} + +bool ParserBase::parseContextHunkHeader() +{ +// kdDebug(8101) << "ParserBase::parseContextHunkHeader()" << endl; + + if ( m_diffIterator == m_diffLines.end() ) + return false; + + if ( !m_contextHunkHeader1.exactMatch( *(m_diffIterator) ) ) + return false; // big fat trouble, aborting... + + ++m_diffIterator; + + if ( m_diffIterator == m_diffLines.end() ) + return false; + + if ( !m_contextHunkHeader2.exactMatch( *(m_diffIterator) ) ) + return false; // big fat trouble, aborting... + + ++m_diffIterator; + + return true; +} + +bool ParserBase::parseEdHunkHeader() +{ + return false; +} + +bool ParserBase::parseNormalHunkHeader() +{ +// kdDebug(8101) << "ParserBase::parseNormalHunkHeader()" << endl; + if ( m_diffIterator != m_diffLines.end() ) + { +// kdDebug(8101) << "Header = " << *m_diffIterator << endl; + if ( m_normalHunkHeaderAdded.exactMatch( *m_diffIterator ) ) + { + m_normalDiffType = Difference::Insert; + } + else if ( m_normalHunkHeaderRemoved.exactMatch( *m_diffIterator ) ) + { + m_normalDiffType = Difference::Delete; + } + else if ( m_normalHunkHeaderChanged.exactMatch( *m_diffIterator ) ) + { + m_normalDiffType = Difference::Change; + } + else + return false; + + ++m_diffIterator; + return true; + } + + return false; +} + +bool ParserBase::parseRCSHunkHeader() +{ + return false; +} + +bool ParserBase::parseUnifiedHunkHeader() +{ +// kdDebug(8101) << "ParserBase::parseUnifiedHunkHeader()" << endl; + + if ( m_unifiedHunkHeader.exactMatch( *m_diffIterator ) ) + { + ++m_diffIterator; + return true; + } + else + { +// kdDebug(8101) << "This is not a unified hunk header : " << (*m_diffIterator) << endl; + return false; + } + +} + +bool ParserBase::parseContextHunkBody() +{ +// kdDebug(8101) << "ParserBase::parseContextHunkBody()" << endl; + + // Storing the src part of the hunk for later use + QStringList oldLines; + for( ; m_diffIterator != m_diffLines.end() && m_contextHunkBodyLine.exactMatch( *m_diffIterator ); ++m_diffIterator ) { +// kdDebug(8101) << "Added old line: " << *m_diffIterator << endl; + oldLines.append( *m_diffIterator ); + } + + if( !m_contextHunkHeader3.exactMatch( *m_diffIterator ) ) + return false; + + ++m_diffIterator; + + // Storing the dest part of the hunk for later use + QStringList newLines; + for( ; m_diffIterator != m_diffLines.end() && m_contextHunkBodyLine.exactMatch( *m_diffIterator ); ++m_diffIterator ) { +// kdDebug(8101) << "Added new line: " << *m_diffIterator << endl; + newLines.append( *m_diffIterator ); + } + + QString function = m_contextHunkHeader1.cap( 1 ); +// kdDebug(8101) << "Captured function: " << function << endl; + int linenoA = m_contextHunkHeader2.cap( 1 ).toInt(); +// kdDebug(8101) << "Source line number: " << linenoA << endl; + int linenoB = m_contextHunkHeader3.cap( 1 ).toInt(); +// kdDebug(8101) << "Dest line number: " << linenoB << endl; + + DiffHunk* hunk = new DiffHunk( linenoA, linenoB, function ); + + m_currentModel->addHunk( hunk ); + + QStringList::Iterator oldIt = oldLines.begin(); + QStringList::Iterator newIt = newLines.begin(); + + Difference* diff; + while( oldIt != oldLines.end() || newIt != newLines.end() ) + { + if( oldIt != oldLines.end() && m_contextHunkBodyRemoved.exactMatch( *oldIt ) ) + { +// kdDebug(8101) << "Delete: " << endl; + diff = new Difference( linenoA, linenoB ); + diff->setType( Difference::Delete ); + m_currentModel->addDiff( diff ); +// kdDebug(8101) << "Difference added" << endl; + hunk->add( diff ); + for( ; oldIt != oldLines.end() && m_contextHunkBodyRemoved.exactMatch( *oldIt ); ++oldIt ) + { +// kdDebug(8101) << " " << m_contextHunkBodyRemoved.cap( 1 ) << endl; + diff->addSourceLine( m_contextHunkBodyRemoved.cap( 1 ) ); + linenoA++; + } + } + else if( newIt != newLines.end() && m_contextHunkBodyAdded.exactMatch( *newIt ) ) + { +// kdDebug(8101) << "Insert: " << endl; + diff = new Difference( linenoA, linenoB ); + diff->setType( Difference::Insert ); + m_currentModel->addDiff( diff ); +// kdDebug(8101) << "Difference added" << endl; + hunk->add( diff ); + for( ; newIt != newLines.end() && m_contextHunkBodyAdded.exactMatch( *newIt ); ++newIt ) + { +// kdDebug(8101) << " " << m_contextHunkBodyAdded.cap( 1 ) << endl; + diff->addDestinationLine( m_contextHunkBodyAdded.cap( 1 ) ); + linenoB++; + } + } + else if( ( oldIt == oldLines.end() || m_contextHunkBodyContext.exactMatch( *oldIt ) ) && + ( newIt == newLines.end() || m_contextHunkBodyContext.exactMatch( *newIt ) ) ) + { +// kdDebug(8101) << "Unchanged: " << endl; + diff = new Difference( linenoA, linenoB ); + // Dont add this diff with addDiff to the model... no unchanged differences allowed in there... + diff->setType( Difference::Unchanged ); + hunk->add( diff ); + while( ( oldIt == oldLines.end() || m_contextHunkBodyContext.exactMatch( *oldIt ) ) && + ( newIt == newLines.end() || m_contextHunkBodyContext.exactMatch( *newIt ) ) && + ( oldIt != oldLines.end() || newIt != newLines.end() ) ) + { + QString l; + if( oldIt != oldLines.end() ) + { + l = m_contextHunkBodyContext.cap( 1 ); +// kdDebug(8101) << "old: " << l << endl; + ++oldIt; + } + if( newIt != newLines.end() ) + { + l = m_contextHunkBodyContext.cap( 1 ); +// kdDebug(8101) << "new: " << l << endl; + ++newIt; + } + diff->addSourceLine( l ); + diff->addDestinationLine( l ); + linenoA++; + linenoB++; + } + } + else if( ( oldIt != oldLines.end() && m_contextHunkBodyChanged.exactMatch( *oldIt ) ) || + ( newIt != newLines.end() && m_contextHunkBodyChanged.exactMatch( *newIt ) ) ) + { +// kdDebug(8101) << "Changed: " << endl; + diff = new Difference( linenoA, linenoB ); + diff->setType( Difference::Change ); + m_currentModel->addDiff( diff ); +// kdDebug(8101) << "Difference added" << endl; + hunk->add( diff ); + while( oldIt != oldLines.end() && m_contextHunkBodyChanged.exactMatch( *oldIt ) ) + { +// kdDebug(8101) << " " << m_contextHunkBodyChanged.cap( 1 ) << endl; + diff->addSourceLine( m_contextHunkBodyChanged.cap( 1 ) ); + linenoA++; + ++oldIt; + } + while( newIt != newLines.end() && m_contextHunkBodyChanged.exactMatch( *newIt ) ) + { +// kdDebug(8101) << " " << m_contextHunkBodyChanged.cap( 1 ) << endl; + diff->addDestinationLine( m_contextHunkBodyChanged.cap( 1 ) ); + linenoB++; + ++newIt; + } + } + else + return false; + diff->determineInlineDifferences(); + } + + return true; +} + +bool ParserBase::parseEdHunkBody() +{ + return false; +} + +bool ParserBase::parseNormalHunkBody() +{ +// kdDebug(8101) << "ParserBase::parseNormalHunkBody" << endl; + + QString type = QString::null; + + int linenoA = 0, linenoB = 0; + + if ( m_normalDiffType == Difference::Insert ) + { + linenoA = m_normalHunkHeaderAdded.cap( 1 ).toInt(); + linenoB = m_normalHunkHeaderAdded.cap( 2 ).toInt(); + } + else if ( m_normalDiffType == Difference::Delete ) + { + linenoA = m_normalHunkHeaderRemoved.cap( 1 ).toInt(); + linenoB = m_normalHunkHeaderRemoved.cap( 3 ).toInt(); + } + else if ( m_normalDiffType == Difference::Change ) + { + linenoA = m_normalHunkHeaderChanged.cap( 1 ).toInt(); + linenoB = m_normalHunkHeaderChanged.cap( 3 ).toInt(); + } + + DiffHunk* hunk = new DiffHunk( linenoA, linenoB ); + m_currentModel->addHunk( hunk ); + Difference* diff = new Difference( linenoA, linenoB ); + hunk->add( diff ); + m_currentModel->addDiff( diff ); + + diff->setType( m_normalDiffType ); + + if ( m_normalDiffType == Difference::Change || m_normalDiffType == Difference::Delete ) + for( ; m_diffIterator != m_diffLines.end() && m_normalHunkBodyRemoved.exactMatch( *m_diffIterator ); ++m_diffIterator ) + { +// kdDebug(8101) << "Line = " << *m_diffIterator << endl; + diff->addSourceLine( m_normalHunkBodyRemoved.cap( 1 ) ); + } + if ( m_normalDiffType == Difference::Change ) + if( m_diffIterator != m_diffLines.end() && m_normalHunkBodyDivider.exactMatch( *m_diffIterator ) ) + { +// kdDebug(8101) << "Line = " << *m_diffIterator << endl; + ++m_diffIterator; + } + else + return false; + if ( m_normalDiffType == Difference::Insert || m_normalDiffType == Difference::Change ) + for( ; m_diffIterator != m_diffLines.end() && m_normalHunkBodyAdded.exactMatch( *m_diffIterator ); ++m_diffIterator ) + { +// kdDebug(8101) << "Line = " << *m_diffIterator << endl; + diff->addDestinationLine( m_normalHunkBodyAdded.cap( 1 ) ); + } + + return true; +} + +bool ParserBase::parseRCSHunkBody() +{ + return false; +} + +bool ParserBase::matchesUnifiedHunkLine( QString line ) const +{ + static const QChar context( ' ' ); + static const QChar added ( '+' ); + static const QChar removed( '-' ); + + QChar first = line[0]; + + return ( first == context || first == added || first == removed ); +} + +bool ParserBase::parseUnifiedHunkBody() +{ +// kdDebug(8101) << "ParserBase::parseUnifiedHunkBody" << endl; + + int linenoA = 0, linenoB = 0; + bool wasNum; + + // Fetching the stuff we need from the hunkheader regexp that was parsed in parseUnifiedHunkHeader(); + linenoA = m_unifiedHunkHeader.cap( 1 ).toInt(); + if( !m_unifiedHunkHeader.cap( 3 ).isEmpty() && m_unifiedHunkHeader.cap( 3 ).toInt(&wasNum) == 0 ) { + // If a hunk is an insertion or deletion with no context, the line number given + // is the one before the hunk. this isn't what we want, so increment it to fix this. + if( wasNum == false ) + return false; + linenoA++; + } + linenoB = m_unifiedHunkHeader.cap( 4 ).toInt(); + if( !m_unifiedHunkHeader.cap( 6 ).isEmpty() && m_unifiedHunkHeader.cap( 6 ).toInt(&wasNum) == 0 ) { + // see above + if( wasNum == false ) + return false; + linenoB++; + } + QString function = m_unifiedHunkHeader.cap( 7 ); + for ( int i = 0; i < 9; i++ ) + { +// kdDebug(8101) << "Capture " << i << ": " << m_unifiedHunkHeader.cap( i ) << endl; + } + + DiffHunk* hunk = new DiffHunk( linenoA, linenoB, function ); + m_currentModel->addHunk( hunk ); + + const QStringList::ConstIterator m_diffLinesEnd = m_diffLines.end(); + + const QString context = QString( " " ); + const QString added = QString( "+" ); + const QString removed = QString( "-" ); + + while( m_diffIterator != m_diffLinesEnd && matchesUnifiedHunkLine( *m_diffIterator ) ) + { + Difference* diff = new Difference( linenoA, linenoB ); + hunk->add( diff ); + + if( (*m_diffIterator).startsWith( context ) ) + { // context + for( ; m_diffIterator != m_diffLinesEnd && (*m_diffIterator).startsWith( context ); ++m_diffIterator ) + { + diff->addSourceLine( QString( *m_diffIterator ).remove( 0, 1 ) ); + diff->addDestinationLine( QString( *m_diffIterator ).remove( 0, 1 ) ); + linenoA++; + linenoB++; + } + } + else + { // This is a real difference, not context + for( ; m_diffIterator != m_diffLinesEnd && (*m_diffIterator).startsWith( removed ); ++m_diffIterator ) + { + diff->addSourceLine( QString( *m_diffIterator ).remove( 0, 1 ) ); + linenoA++; + } + for( ; m_diffIterator != m_diffLinesEnd && (*m_diffIterator).startsWith( added ); ++m_diffIterator ) + { + diff->addDestinationLine( QString( *m_diffIterator ).remove( 0, 1 ) ); + linenoB++; + } + if ( diff->sourceLineCount() == 0 ) + { + diff->setType( Difference::Insert ); +// kdDebug(8101) << "Insert difference" << endl; + } + else if ( diff->destinationLineCount() == 0 ) + { + diff->setType( Difference::Delete ); +// kdDebug(8101) << "Delete difference" << endl; + } + else + { + diff->setType( Difference::Change ); +// kdDebug(8101) << "Change difference" << endl; + } + diff->determineInlineDifferences(); + m_currentModel->addDiff( diff ); + } + } + + return true; +} + +DiffModelList* ParserBase::parseContext() +{ + while ( parseContextDiffHeader() ) + { + while ( parseContextHunkHeader() ) + parseContextHunkBody(); + if ( m_currentModel->differenceCount() > 0 ) + m_models->append( m_currentModel ); + } + + m_models->sort(); + + if ( m_models->count() > 0 ) + { + return m_models; + } + else + { + delete m_models; + return 0L; + } +} + +DiffModelList* ParserBase::parseEd() +{ + while ( parseEdDiffHeader() ) + { + while ( parseEdHunkHeader() ) + parseEdHunkBody(); + if ( m_currentModel->differenceCount() > 0 ) + m_models->append( m_currentModel ); + } + + m_models->sort(); + + if ( m_models->count() > 0 ) + { + return m_models; + } + else + { + delete m_models; + return 0L; + } +} + +DiffModelList* ParserBase::parseNormal() +{ + while ( parseNormalDiffHeader() ) + { + while ( parseNormalHunkHeader() ) + parseNormalHunkBody(); + if ( m_currentModel->differenceCount() > 0 ) + m_models->append( m_currentModel ); + } + + if ( m_singleFileDiff ) + { + while ( parseNormalHunkHeader() ) + parseNormalHunkBody(); + if ( m_currentModel->differenceCount() > 0 ) + m_models->append( m_currentModel ); + } + + m_models->sort(); + + if ( m_models->count() > 0 ) + { + return m_models; + } + else + { + delete m_models; + return 0L; + } +} + +DiffModelList* ParserBase::parseRCS() +{ + while ( parseRCSDiffHeader() ) + { + while ( parseRCSHunkHeader() ) + parseRCSHunkBody(); + if ( m_currentModel->differenceCount() > 0 ) + m_models->append( m_currentModel ); + } + + m_models->sort(); + + if ( m_models->count() > 0 ) + { + return m_models; + } + else + { + delete m_models; + return 0L; + } +} + +DiffModelList* ParserBase::parseUnified() +{ + while ( parseUnifiedDiffHeader() ) + { + while ( parseUnifiedHunkHeader() ) + parseUnifiedHunkBody(); +// kdDebug(8101) << "New model ready to be analyzed..." << endl; +// kdDebug(8101) << " differenceCount() == " << m_currentModel->differenceCount() << endl; + if ( m_currentModel->differenceCount() > 0 ) + m_models->append( m_currentModel ); + } + + m_models->sort(); + + if ( m_models->count() > 0 ) + { + return m_models; + } + else + { + delete m_models; + return 0L; + } +} + diff --git a/kompare/libdiff2/parserbase.h b/kompare/libdiff2/parserbase.h new file mode 100644 index 00000000..5e08803e --- /dev/null +++ b/kompare/libdiff2/parserbase.h @@ -0,0 +1,133 @@ +/************************************************************************** +** parserbase.h +** ------------------- +** begin : Tue Jul 30 23:53:52 2002 +** copyright : (C) 2002-2004 Otto Bruggeman +** email : otto.bruggeman@home.nl +** +***************************************************************************/ +/*************************************************************************** +** +** This program 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 2 of the License, or +** ( at your option ) any later version. +** +***************************************************************************/ + +#ifndef _DIFF2_PARSERBASE_H +#define _DIFF2_PARSERBASE_H + +#include <qregexp.h> + +#include "kompare.h" +#include "difference.h" +#include "diffmodellist.h" + +class QStringList; +class QString; + +namespace Diff2 +{ + +class KompareModelList; + +class ParserBase +{ +public: + ParserBase( const KompareModelList* list, const QStringList& diff ); + virtual ~ParserBase(); + +public: + enum Kompare::Format format() { return determineFormat(); }; + DiffModelList* parse(); + +protected: + virtual bool parseContextDiffHeader(); + virtual bool parseEdDiffHeader(); + virtual bool parseNormalDiffHeader(); + virtual bool parseRCSDiffHeader(); + virtual bool parseUnifiedDiffHeader(); + + virtual bool parseContextHunkHeader(); + virtual bool parseEdHunkHeader(); + virtual bool parseNormalHunkHeader(); + virtual bool parseRCSHunkHeader(); + virtual bool parseUnifiedHunkHeader(); + + virtual bool parseContextHunkBody(); + virtual bool parseEdHunkBody(); + virtual bool parseNormalHunkBody(); + virtual bool parseRCSHunkBody(); + virtual bool parseUnifiedHunkBody(); + + virtual DiffModelList* parseContext(); + virtual DiffModelList* parseEd(); + virtual DiffModelList* parseNormal(); + virtual DiffModelList* parseRCS(); + virtual DiffModelList* parseUnified(); + +protected: // Helper methods to speed things up + bool matchesUnifiedHunkLine( QString line ) const; + +protected: + /** What is format of the diff */ + virtual enum Kompare::Format determineFormat(); + +protected: + // Regexps for context parsing + QRegExp m_contextDiffHeader1; + QRegExp m_contextDiffHeader2; + + QRegExp m_contextHunkHeader1; + QRegExp m_contextHunkHeader2; + QRegExp m_contextHunkHeader3; + + QRegExp m_contextHunkBodyRemoved; + QRegExp m_contextHunkBodyAdded; + QRegExp m_contextHunkBodyChanged; + QRegExp m_contextHunkBodyContext; + QRegExp m_contextHunkBodyLine; // Added for convenience + + // Regexps for normal parsing + QRegExp m_normalDiffHeader; + + QRegExp m_normalHunkHeaderAdded; + QRegExp m_normalHunkHeaderRemoved; + QRegExp m_normalHunkHeaderChanged; + + QRegExp m_normalHunkBodyRemoved; + QRegExp m_normalHunkBodyAdded; + QRegExp m_normalHunkBodyDivider; + + enum Difference::Type m_normalDiffType; + + // RegExps for rcs parsing + QRegExp m_rcsDiffHeader; + + // Regexps for unified parsing + QRegExp m_unifiedDiffHeader1; + QRegExp m_unifiedDiffHeader2; + + QRegExp m_unifiedHunkHeader; + + QRegExp m_unifiedHunkBodyAdded; + QRegExp m_unifiedHunkBodyRemoved; + QRegExp m_unifiedHunkBodyContext; + QRegExp m_unifiedHunkBodyLine; // Added for convenience + +protected: + const QStringList& m_diffLines; + DiffModel* m_currentModel; + DiffModelList* m_models; + QStringList::ConstIterator m_diffIterator; + + bool m_singleFileDiff; + +protected: + const KompareModelList* m_list; +}; + +} // End of namespace Diff2 + +#endif diff --git a/kompare/libdiff2/perforceparser.cpp b/kompare/libdiff2/perforceparser.cpp new file mode 100644 index 00000000..907d88ff --- /dev/null +++ b/kompare/libdiff2/perforceparser.cpp @@ -0,0 +1,223 @@ +/************************************************************************** +** perforceparser.cpp +** ------------------ +** begin : Sun Aug 4 15:05:35 2002 +** copyright : (C) 2002-2004 Otto Bruggeman +** email : otto.bruggeman@home.nl +** +***************************************************************************/ +/*************************************************************************** +** +** This program 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 2 of the License, or +** ( at your option ) any later version. +** +***************************************************************************/ + +#include <qregexp.h> + +#include <kdebug.h> + +#include "perforceparser.h" + +using namespace Diff2; + +PerforceParser::PerforceParser( const KompareModelList* list, const QStringList& diff ) : ParserBase( list, diff ) +{ + m_contextDiffHeader1.setPattern( "==== (.*) - (.*) ====\\n" ); + m_contextDiffHeader1.setMinimal( true ); + m_normalDiffHeader.setPattern ( "==== (.*) - (.*) ====\\n" ); + m_normalDiffHeader.setMinimal ( true ); + m_rcsDiffHeader.setPattern ( "==== (.*) - (.*) ====\\n" ); + m_rcsDiffHeader.setMinimal ( true ); + m_unifiedDiffHeader1.setPattern( "==== (.*) - (.*) ====\\n" ); + m_unifiedDiffHeader1.setMinimal( true ); +} + +PerforceParser::~PerforceParser() +{ +} + +enum Kompare::Format PerforceParser::determineFormat() +{ + kdDebug(8101) << "Determining the format of the Perforce Diff" << endl; + + QRegExp unifiedRE( "^@@" ); + QRegExp contextRE( "^\\*{15}" ); + QRegExp normalRE ( "^\\d+(|,\\d+)[acd]\\d+(|,\\d+)" ); + QRegExp rcsRE ( "^[acd]\\d+ \\d+" ); + // Summary is not supported since it gives no useful parsable info + + QStringList::ConstIterator it = m_diffLines.begin(); + + while( it != m_diffLines.end() ) + { + if( (*it).find( unifiedRE, 0 ) == 0 ) + { + kdDebug(8101) << "Difflines are from a Unified diff..." << endl; + return Kompare::Unified; + } + else if( (*it).find( contextRE, 0 ) == 0 ) + { + kdDebug(8101) << "Difflines are from a Context diff..." << endl; + return Kompare::Context; + } + else if( (*it).find( normalRE, 0 ) == 0 ) + { + kdDebug(8101) << "Difflines are from a Normal diff..." << endl; + return Kompare::Normal; + } + else if( (*it).find( rcsRE, 0 ) == 0 ) + { + kdDebug(8101) << "Difflines are from a RCS diff..." << endl; + return Kompare::RCS; + } + ++it; + } + kdDebug(8101) << "Difflines are from an unknown diff..." << endl; + return Kompare::UnknownFormat; +} + +bool PerforceParser::parseContextDiffHeader() +{ +// kdDebug(8101) << "ParserBase::parseContextDiffHeader()" << endl; + bool result = false; + + QStringList::ConstIterator itEnd = m_diffLines.end(); + + QRegExp sourceFileRE ( "([^\\#]+)#(\\d+)" ); + QRegExp destinationFileRE( "([^\\#]+)#(|\\d+)" ); + + while ( m_diffIterator != itEnd ) + { + if ( m_contextDiffHeader1.exactMatch( *(m_diffIterator)++ ) ) + { +// kdDebug(8101) << "Matched length Header1 = " << m_contextDiffHeader1.matchedLength() << endl; +// kdDebug(8101) << "Matched string Header1 = " << m_contextDiffHeader1.cap( 0 ) << endl; +// kdDebug(8101) << "First capture Header1 = " << m_contextDiffHeader1.cap( 1 ) << endl; +// kdDebug(8101) << "Second capture Header1 = " << m_contextDiffHeader1.cap( 2 ) << endl; + + m_currentModel = new DiffModel(); + sourceFileRE.exactMatch( m_contextDiffHeader1.cap( 1 ) ); + destinationFileRE.exactMatch( m_contextDiffHeader1.cap( 2 ) ); + kdDebug(8101) << "Matched length = " << sourceFileRE.matchedLength() << endl; + kdDebug(8101) << "Matched length = " << destinationFileRE.matchedLength() << endl; + kdDebug(8101) << "Captured texts = " << sourceFileRE.capturedTexts() << endl; + kdDebug(8101) << "Captured texts = " << destinationFileRE.capturedTexts() << endl; + kdDebug(8101) << "Source File : " << sourceFileRE.cap( 1 ) << endl; + kdDebug(8101) << "Destination File : " << destinationFileRE.cap( 1 ) << endl; + m_currentModel->setSourceFile ( sourceFileRE.cap( 1 ) ); + m_currentModel->setDestinationFile( destinationFileRE.cap( 1 ) ); + + result = true; + + break; + } + else + { + kdDebug(8101) << "Matched length = " << m_contextDiffHeader1.matchedLength() << endl; + kdDebug(8101) << "Captured texts = " << m_contextDiffHeader1.capturedTexts() << endl; + } + } + + return result; +} + +bool PerforceParser::parseNormalDiffHeader() +{ + bool result = false; + + QStringList::ConstIterator itEnd = m_diffLines.end(); + + QRegExp sourceFileRE ( "([^\\#]+)#(\\d+)" ); + QRegExp destinationFileRE( "([^\\#]+)#(|\\d+)" ); + + while ( m_diffIterator != itEnd ) + { + kdDebug(8101) << "Line = " << *m_diffIterator << endl; + kdDebug(8101) << "String length = " << (*m_diffIterator).length() << endl; + if ( m_normalDiffHeader.exactMatch( *(m_diffIterator)++ ) ) + { + kdDebug(8101) << "Matched length Header1 = " << m_normalDiffHeader.matchedLength() << endl; + kdDebug(8101) << "Matched string Header1 = " << m_normalDiffHeader.cap( 0 ) << endl; + kdDebug(8101) << "First capture Header1 = \"" << m_normalDiffHeader.cap( 1 ) << "\"" << endl; + kdDebug(8101) << "Second capture Header1 = \"" << m_normalDiffHeader.cap( 2 ) << "\"" << endl; + + m_currentModel = new DiffModel(); + sourceFileRE.exactMatch( m_normalDiffHeader.cap( 1 ) ); + destinationFileRE.exactMatch( m_normalDiffHeader.cap( 2 ) ); + kdDebug(8101) << "Matched length = " << sourceFileRE.matchedLength() << endl; + kdDebug(8101) << "Matched length = " << destinationFileRE.matchedLength() << endl; + kdDebug(8101) << "Captured texts = " << sourceFileRE.capturedTexts() << endl; + kdDebug(8101) << "Captured texts = " << destinationFileRE.capturedTexts() << endl; + kdDebug(8101) << "Source File : " << sourceFileRE.cap( 1 ) << endl; + kdDebug(8101) << "Destination File : " << destinationFileRE.cap( 1 ) << endl; + m_currentModel->setSourceFile ( sourceFileRE.cap( 1 ) ); + m_currentModel->setDestinationFile( destinationFileRE.cap( 1 ) ); + + result = true; + + break; + } + else + { + kdDebug(8101) << "Matched length = " << m_normalDiffHeader.matchedLength() << endl; + kdDebug(8101) << "Captured texts = " << m_normalDiffHeader.capturedTexts() << endl; + } + } + + return result; +} + +bool PerforceParser::parseRCSDiffHeader() +{ + return false; +} + +bool PerforceParser::parseUnifiedDiffHeader() +{ + bool result = false; + + QStringList::ConstIterator itEnd = m_diffLines.end(); + + QRegExp sourceFileRE ( "([^\\#]+)#(\\d+)" ); + QRegExp destinationFileRE( "([^\\#]+)#(|\\d+)" ); + + while ( m_diffIterator != itEnd ) + { +// kdDebug(8101) << "Line = " << *m_diffIterator << endl; +// kdDebug(8101) << "String length = " << (*m_diffIterator).length() << endl; + if ( m_unifiedDiffHeader1.exactMatch( *(m_diffIterator)++ ) ) + { +// kdDebug(8101) << "Matched length Header1 = " << m_unifiedDiffHeader1.matchedLength() << endl; +// kdDebug(8101) << "Matched string Header1 = " << m_unifiedDiffHeader1.cap( 0 ) << endl; +// kdDebug(8101) << "First capture Header1 = \"" << m_unifiedDiffHeader1.cap( 1 ) << "\"" << endl; +// kdDebug(8101) << "Second capture Header1 = \"" << m_unifiedDiffHeader1.cap( 2 ) << "\"" << endl; + + m_currentModel = new DiffModel(); + sourceFileRE.exactMatch( m_unifiedDiffHeader1.cap( 1 ) ); + destinationFileRE.exactMatch( m_unifiedDiffHeader1.cap( 2 ) ); +// kdDebug(8101) << "Matched length = " << sourceFileRE.matchedLength() << endl; +// kdDebug(8101) << "Matched length = " << destinationFileRE.matchedLength() << endl; +// kdDebug(8101) << "Captured texts = " << sourceFileRE.capturedTexts() << endl; +// kdDebug(8101) << "Captured texts = " << destinationFileRE.capturedTexts() << endl; +// kdDebug(8101) << "Source File : " << sourceFileRE.cap( 1 ) << endl; +// kdDebug(8101) << "Destination File : " << destinationFileRE.cap( 1 ) << endl; + m_currentModel->setSourceFile ( sourceFileRE.cap( 1 ) ); + m_currentModel->setDestinationFile( destinationFileRE.cap( 1 ) ); + + result = true; + + break; + } + else + { +// kdDebug(8101) << "Matched length = " << m_unifiedDiffHeader1.matchedLength() << endl; +// kdDebug(8101) << "Captured texts = " << m_unifiedDiffHeader1.capturedTexts() << endl; + } + } + + return result; +} + diff --git a/kompare/libdiff2/perforceparser.h b/kompare/libdiff2/perforceparser.h new file mode 100644 index 00000000..99973167 --- /dev/null +++ b/kompare/libdiff2/perforceparser.h @@ -0,0 +1,44 @@ +/************************************************************************** +** perforceparser.h +** ------------------- +** begin : Sun Sep 8 20:58:59 2002 +** copyright : (c) 2002-2004 Otto Bruggeman +** email : otto.bruggeman@home.nl +** +***************************************************************************/ +/*************************************************************************** +** +** This program 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 2 of the License, or +** ( at your option ) any later version. +** +***************************************************************************/ + +#ifndef _PERFORCE_PARSER_H +#define _PERFORCE_PARSER_H + +#include "parserbase.h" + +namespace Diff2 +{ + +class PerforceParser : public ParserBase +{ +public: + PerforceParser( const KompareModelList* list, const QStringList& diff ); + virtual ~PerforceParser(); + +protected: + virtual bool parseContextDiffHeader(); + virtual bool parseNormalDiffHeader(); + virtual bool parseRCSDiffHeader(); + virtual bool parseUnifiedDiffHeader(); + +protected: + virtual enum Kompare::Format determineFormat(); +}; + +} // End of namespace Diff2 + +#endif |