/* ==================================================================== * Copyright (c) 2003-2006, Martin Hauner * http://subcommander.tigris.org * * Subcommander is licensed as described in the file doc/COPYING, which * you should have received as part of this distribution. * ==================================================================== */ // sc #include "Diff3.h" #include "LineTokenizer.h" #include "DiffBaton.h" #include "DiffInfoTarget.h" #include "DiffInfoModelImpl.h" #include "sublib/Line.h" #include "sublib/LineTarget.h" #include "sublib/ConflictType.h" #include "sublib/TextModelImpl.h" #include "sublib/NullTextModel.h" #include "Merge.h" #include "svn/Diff.h" #include "svn/Error.h" #include "util/iconvstream.h" #include "util/Error.h" // apr #include // sys #include #include // TODO fix code duplication with OutBaton class OutBaton3 : public svn::OutputBaton { public: enum Source { srcOriginal = svn::Diff::srcOriginal, srcModified = svn::Diff::srcModified, srcLatest = svn::Diff::srcLatest, srcAncestor = svn::Diff::srcAncestor, srcMax = srcAncestor + 1 }; OutBaton3( Tokenizer** tokenizer, LineTarget** targets, DiffInfoTarget* infos, apr_pool_t *pool = 0 ) : _tokenizer(tokenizer), _targets(targets), _infos(infos), _blockCnt(1), _blockStart(0) { assert(_tokenizer); assert(_targets); apr_status_t status = apr_pool_create(&_pool,pool); assert( status == APR_SUCCESS ); } ~OutBaton3() { apr_pool_destroy(_pool); } // common lines in all datasources sc::Error* common( const svn::DiffOffsets& offs ) { for( svn::Offset i = 1; i <= offs._originalLength; i++ ) { addTextLine( srcOriginal, ctCommon ); addTextLine( srcModified, ctCommon ); addTextLine( srcLatest, ctCommon ); } _infos->addDiffInfo( DiffInfo(ctCommon,_blockCnt,createOffsets(offs._originalLength)) ); _blockCnt++; return 0; } // original = latest != modified sc::Error* diffModified( const svn::DiffOffsets& offs ) { bool originalConflictEmpty = offs._originalLength == 0; bool modifiedConflictEmpty = offs._modifiedLength == 0; bool latestConflictEmpty = offs._latestLength == 0; assert( offs._originalLength == offs._latestLength ); svn::Offset max = 0; if( offs._originalLength > max ) max = offs._originalLength; if( offs._modifiedLength > max ) max = offs._modifiedLength; for( svn::Offset i = 1; i <= max; i++ ) { if( i <= offs._originalLength ) { addTextLine( srcOriginal, ctConflictLatestModified ); } else { addControlLine( srcOriginal, ctConflictLatestModifiedEmpty, originalConflictEmpty ); originalConflictEmpty = false; } if( i <= offs._modifiedLength ) { addTextLine( srcModified, ctConflictAll ); } else { addControlLine( srcModified, ctConflictAllEmpty, modifiedConflictEmpty ); modifiedConflictEmpty = false; } if( i <= offs._latestLength ) { addTextLine( srcLatest, ctConflictLatestModified ); } else { addControlLine( srcLatest, ctConflictLatestModifiedEmpty, latestConflictEmpty ); latestConflictEmpty = false; } } _infos->addDiffInfo( DiffInfo(ctConflictLatestModified,_blockCnt,createOffsets(max)) ); _blockCnt++; return 0; } // original = modified != latest sc::Error* diffLatest( const svn::DiffOffsets& offs ) { assert( offs._originalLength == offs._modifiedLength ); bool originalConflictEmpty = offs._originalLength == 0; bool modifiedConflictEmpty = offs._modifiedLength == 0; bool latestConflictEmpty = offs._latestLength == 0; svn::Offset max = 0; if( offs._originalLength > max ) max = offs._originalLength; if( offs._latestLength > max ) max = offs._latestLength; for( svn::Offset i = 1; i <= max; i++ ) { if( i <= offs._originalLength ) { addTextLine( srcOriginal, ctConflictModifiedLatest ); } else { addControlLine( srcOriginal, ctConflictModifiedLatestEmpty, originalConflictEmpty ); originalConflictEmpty = false; } if( i <= offs._modifiedLength ) { addTextLine( srcModified, ctConflictModifiedLatest ); } else { addControlLine( srcModified, ctConflictModifiedLatestEmpty, modifiedConflictEmpty ); modifiedConflictEmpty = false; } if( i <= offs._latestLength ) { addTextLine( srcLatest, ctConflictAll ); } else { addControlLine( srcLatest, ctConflictAllEmpty, latestConflictEmpty ); latestConflictEmpty = false; } } _infos->addDiffInfo( DiffInfo(ctConflictModifiedLatest,_blockCnt,createOffsets(max)) ); _blockCnt++; return 0; } // original != lastest = modified sc::Error* diffCommon( const svn::DiffOffsets& offs ) { assert( offs._modifiedLength == offs._latestLength ); bool originalConflictEmpty = offs._originalLength == 0; bool modifiedConflictEmpty = offs._modifiedLength == 0; bool latestConflictEmpty = offs._latestLength == 0; svn::Offset max = 0; if( offs._originalLength > max ) max = offs._originalLength; if( offs._modifiedLength > max ) max = offs._modifiedLength; for( svn::Offset i = 1; i <= max; i++ ) { if( i <= offs._originalLength ) { addTextLine( srcOriginal, ctConflictAll ); } else { addControlLine( srcOriginal, ctConflictAllEmpty, originalConflictEmpty ); originalConflictEmpty = false; } if( i <= offs._modifiedLength ) { addTextLine( srcModified, ctConflictOriginal ); } else { addControlLine( srcModified, ctConflictOriginalEmpty, modifiedConflictEmpty ); modifiedConflictEmpty = false; } if( i <= offs._latestLength ) { addTextLine( srcLatest, ctConflictOriginal ); } else { addControlLine( srcLatest, ctConflictOriginalEmpty, latestConflictEmpty ); latestConflictEmpty = false; } } _infos->addDiffInfo( DiffInfo(ctConflictOriginal,_blockCnt,createOffsets(max)) ); _blockCnt++; return 0; } // original != latest != modified sc::Error* conflict( const svn::DiffOffsets& offs, svn::DiffData* resolvedDiff ) { bool originalConflictEmpty = offs._originalLength == 0; bool modifiedConflictEmpty = offs._modifiedLength == 0; bool latestConflictEmpty = offs._latestLength == 0; svn::Offset max = 0; if( offs._originalLength > max ) max = offs._originalLength; if( offs._modifiedLength > max ) max = offs._modifiedLength; if( offs._latestLength > max ) max = offs._latestLength; for( svn::Offset i = 1; i <= max; i++ ) { if( i <= offs._originalLength ) { addTextLine( srcOriginal, ctConflictAll ); } else { addControlLine( srcOriginal, ctConflictAllEmpty, originalConflictEmpty ); originalConflictEmpty = false; } if( i <= offs._modifiedLength ) { addTextLine( srcModified, ctConflictAll ); } else { addControlLine( srcModified, ctConflictAllEmpty, modifiedConflictEmpty ); modifiedConflictEmpty = false; } if( i <= offs._latestLength ) { addTextLine( srcLatest, ctConflictAll ); } else { addControlLine( srcLatest, ctConflictAllEmpty, latestConflictEmpty ); latestConflictEmpty = false; } } _infos->addDiffInfo( DiffInfo(ctConflictAll,_blockCnt,createOffsets(max)) ); _blockCnt++; return 0; } private: void addTextLine( Source src, ConflictType type ) { char *token; size_t size; bool b; b = _tokenizer[src]->nextToken( &token, &size ); assert(b); Line line( sc::String(token,size), _blockCnt, type ); b = _targets[src]->addLine( line ); assert(b); } void addControlLine( Source src, ConflictType cType, bool hasConflictEmpty ) { ConflictType type = ctNop; if( hasConflictEmpty ) { type = cType; } Line line( sc::String(""), _blockCnt, type ); bool b = _targets[src]->addLine( line ); assert(b); } BlockInfo createOffsets( svn::Offset length ) { BlockInfo info(_blockStart,length); _blockStart += length; return info; } private: Tokenizer** _tokenizer; LineTarget** _targets; DiffInfoTarget* _infos; int _blockCnt; svn::Offset _blockStart; apr_pool_t* _pool; }; /////////////////////////////////////////////////////////////////////////////// Diff3::Diff3( const FileDataPtr original, const FileDataPtr modified, const FileDataPtr latest, const sc::String& merged ) : _original(original), _modified(modified), _latest(latest), _merged(merged), _diffInfo(0) { } Diff3::~Diff3() { } const sc::Error* Diff3::diff3( bool ignoreWhitespace ) { svn::Diff diff; svn::DiffData* diffData = 0; sc::Error* err; { LineTokenizer orgTokenizer( _original->getBuffer(), _original->getBufferSize() ); LineTokenizer modTokenizer( _modified->getBuffer(), _modified->getBufferSize() ); LineTokenizer latTokenizer( _latest->getBuffer(), _latest->getBufferSize() ); Tokenizer* tokenizer[OutBaton3::srcMax]; tokenizer[OutBaton3::srcOriginal] = &orgTokenizer; tokenizer[OutBaton3::srcModified] = &modTokenizer; tokenizer[OutBaton3::srcLatest] = &latTokenizer; DiffBaton diffBaton( tokenizer, ignoreWhitespace ); err = diff.diff3( &diffData, &diffBaton ); SC_ERR(err); } { TextModelImpl* orgModel = new TextModelImpl( _original->getName() ); TextModelImpl* modModel = new TextModelImpl( _modified->getName() ); TextModelImpl* latModel = new TextModelImpl( _latest->getName() ); TextModelImpl* merModel = new TextModelImpl( _merged.isEmpty() ? _modified->getName() : _merged ); DiffInfoModelImpl* diffInfoImpl = new DiffInfoModelImpl(); LineTokenizer orgTokenizer( _original->getBuffer(), _original->getBufferSize() ); LineTokenizer modTokenizer( _modified->getBuffer(), _modified->getBufferSize() ); LineTokenizer latTokenizer( _latest->getBuffer(), _latest->getBufferSize() ); Tokenizer* tokenizer[OutBaton3::srcMax]; tokenizer[OutBaton3::srcOriginal] = &orgTokenizer; tokenizer[OutBaton3::srcModified] = &modTokenizer; tokenizer[OutBaton3::srcLatest] = &latTokenizer; LineTarget* targets[OutBaton3::srcMax]; targets[OutBaton3::srcOriginal] = orgModel; targets[OutBaton3::srcModified] = modModel; targets[OutBaton3::srcLatest] = latModel; OutBaton3 outBaton( tokenizer, targets, diffInfoImpl ); err = diffData->output( &outBaton ); SC_ERR(err); diffInfoImpl->setModel( DiffInfoModel::dmOriginal, orgModel ); diffInfoImpl->setModel( DiffInfoModel::dmModified, modModel ); diffInfoImpl->setModel( DiffInfoModel::dmLatest, latModel ); diffInfoImpl->setModel( DiffInfoModel::dmMerged, merModel ); _diffInfo = diffInfoImpl; // automatically merge where possible... Merge m( merModel, _diffInfo ); m.merge(); diffInfoImpl->setConflictCnt( m.getNotMergedCnt() ); } delete diffData; return sc::Success; } // #if 0 { apr_file_t* file; apr_status_t status = apr_file_open( &file, "out.txt", APR_WRITE, APR_OS_DEFAULT, pool ); svn_error_t* error = svn_diff_file_output_merge( file, diffData->_diff, "org.txt", "new.txt", "new2.txt", 0, 0, 0, 0, false, true, pool ); } #endif //