#include "DAZipper.h"
#include <clientapi.h>
#include "DAFile.h"
#include "DAInteg.h"
#include "DALine.h"
#include "DARev.h"
#include "DATooth.h"
struct DAFileStack
{
DAFile* file;
DAFileStack* next;
};
DAZipper::DAZipper( DAFile* f, int r )
: file( f ), rev( r )
{
copy = 0x0;
sources = 0x0;
left = new DATooth( 0x0 );
right = new DATooth( 0x0 );
DARev* drev = file->getRev( rev );
if ( !drev ) return;
bool fileFound;
DAFileStack* newFile;
DAInteg* integ = 0x0;
for ( int i = 0 ; i < drev->integCount() ; i++ )
{
integ = drev->getInteg( i );
if ( !integ->getFile() ) continue;
if ( integ->getHow() == DAInteg::Branch ||
integ->getHow() == DAInteg::Copy )
{
copy = integ;
continue;
}
fileFound = false;
for ( DAFileStack* s = sources ; s ; s = s->next )
{
if ( s->file == integ->getFile() )
fileFound = true;
}
if ( fileFound )
continue;
newFile = new DAFileStack;
newFile->file = integ->getFile();
newFile->next = sources;
sources = newFile;
}
}
DAZipper::~DAZipper(void)
{
DAFileStack* s;
while ( sources )
{
s = sources;
sources = s->next;
delete s;
}
while ( left->nextTooth() != left )
{
delete left->nextTooth();
}
delete left;
while ( right->nextTooth() != right )
{
delete right->nextTooth();
}
delete right;
}
void DAZipper::Zip()
{
// Copies are easy. Handle those first.
if ( copy ) ZipCopy();
// Now build the actual "zipper" halves.
for ( DAFileStack* s = sources ; s ; s = s->next )
{
buildLeftZipper( s->file );
}
if ( left->nextTooth() != left )
buildRightZipper();
// Try context matching.
matchByContext();
// Cut out any remaining teeth that can't possibly match anything.
eliminateUniques();
// Try order matching.
matchByDistance();
// Eliminate anything that's left.
eliminateAll();
}
void DAZipper::ZipCopy()
{
if ( !copy->getFile() ) return;
// A "copy" record implies that this revision is
// a perfect copy of the from revision, with maybe
// the occasional ktext anomaly. As such, we ought
// to be able to match lines in the source and target
// to each other one-for-one.
DALine* src = copy->getFile()->zeroLine()->nextAsOf( copy->getERev() );
DALine* tgt = file->zeroLine()->nextAsOf( rev );
while ( src->text() && tgt->text() )
{
if ( !tgt->zipped() &&
tgt->lowerRev() == rev &&
src->text() == tgt->text() )
{
tgt->setAncestor( src );
}
src = src->nextAsOf( copy->getERev() );
tgt = tgt->nextAsOf( rev );
}
}
void DAZipper::buildLeftZipper( DAFile* source )
{
bool isRelevant;
DAInteg* integ;
int srev, erev;
DARev* drev = file->getRev( rev );
for ( DALine* line = source->zeroLine()->nextLine() ;
line->text() ;
line = line->nextLine() )
{
if ( line->getTooth() ) continue;
isRelevant = false;
for ( int i = 0 ; i < drev->integCount() ; i++ )
{
integ = drev->getInteg( i );
if ( integ->getHow() == DAInteg::Branch ||
integ->getHow() == DAInteg::Copy ||
integ->getHow() == DAInteg::Ignore ) continue;
srev = integ->getSRev();
erev = integ->getERev();
if ( integ->getHow() == DAInteg::Add )
srev = 1;
if ( srev <= line->lowerRev() && //line added at or after srev
erev >= line->lowerRev() && //line added at or before erev
erev <= line->upperRev() ) //line not removed before erev
{
isRelevant = true;
break;
}
}
if ( isRelevant )
{
left->prependTooth( new DATooth( line ) );
}
}
}
void DAZipper::buildRightZipper()
{
for ( DALine* line = file->zeroLine()->nextLine() ;
line->text() ;
line = line->nextLine() )
{
if ( line->lowerRev() != rev ) continue;
if ( line->zipped() ) continue;
right->prependTooth( new DATooth( line ) );
}
}
void DAZipper::eliminateAll()
{
while ( left->nextTooth()->getLine() )
{
delete left->nextTooth();
}
while ( right->nextTooth()->getLine() )
{
if ( right->nextTooth()->getLine()->lowerRev() == rev &&
!right->nextTooth()->getLine()->zipped() )
{
right->nextTooth()->getLine()->setAncestor
( right->nextTooth()->getLine() );
}
delete right->nextTooth();
}
}
void DAZipper::eliminateUniques()
{
DATooth *s, *t;
DATooth* unique = 0x0;
bool hasMatch;
for ( s = left->nextTooth() ; s->getLine() ; s = s->nextTooth() )
{
delete unique;
unique = 0x0;
hasMatch = false;
for ( t = right->nextTooth() ; t->getLine() ; t = t->nextTooth() )
{
if ( s->getLine()->text() == t->getLine()->text() )
{
hasMatch = true;
break;
}
}
if ( !hasMatch ) unique = s;
}
delete unique;
unique = 0x0;
for ( s = right->nextTooth() ; s->getLine() ; s = s->nextTooth() )
{
delete unique;
unique = 0x0;
hasMatch = false;
for ( t = left->nextTooth() ; t->getLine() ; t = t->nextTooth() )
{
if ( s->getLine()->text() == t->getLine()->text() )
{
hasMatch = true;
break;
}
}
if ( !hasMatch ) unique = s;
}
delete unique;
}
void DAZipper::matchByContext()
{
DATooth* t = right->nextTooth();
DALine* ancLine;
DATooth* ancTooth;
while ( t->getLine() )
{
ancLine = 0x0;
ancTooth = 0x0;
if ( ( (ancLine = t->getLine()->prevAsOf( rev )->ancestor()) &&
(ancTooth = ancLine->nextToothed()->getTooth()) &&
ancTooth->getLine()->text() == t->getLine()->text() ) ||
( (ancLine = t->getLine()->nextAsOf( rev )->ancestor()) &&
(ancTooth = ancLine->prevToothed()->getTooth()) &&
ancTooth->getLine()->text() == t->getLine()->text() ) )
{
t->getLine()->setAncestor( ancTooth->getLine() );
delete t;
delete ancTooth;
ancTooth = 0x0;
t = right;
}
t = t->nextTooth();
}
}
void DAZipper::matchByDistance()
{
DATooth* l = left->nextTooth();
DATooth* r = right->nextTooth();
DATooth *lc, *rc;
DATooth *lm, *rm;
while ( l->getLine() && r->getLine() )
{
lc = r;
rc = l;
lm = rm = 0x0;
while ( lc->getLine() || rc->getLine() )
{
if ( lc->getLine() )
{
if ( lc->getLine()->text() == l->getLine()->text() )
break;
else
lc = lc->nextTooth();
}
if ( rc->getLine() )
{
if ( rc->getLine()->text() == r->getLine()->text() )
break;
else
rc = rc->nextTooth();
}
}
if ( lc->getLine() && l->getLine()->text() == lc->getLine()->text() )
{
lm = l;
rm = lc;
}
if ( rc->getLine() && rc->getLine()->text() == r->getLine()->text() )
{
lm = rc;
rm = r;
}
if ( lm && rm )
{
l = lm->nextTooth();
r = rm->nextTooth();
rm->getLine()->setAncestor( lm->getLine() );
delete lm;
delete rm;
}
else
{
l = l->nextTooth();
r = r->nextTooth();
}
}
}
| # | Change | User | Description | Committed | |
|---|---|---|---|---|---|
| #2 | 6298 | Sam Stafford |
Add some new sanity checks. One prevents an infaloop observed while testing against a file with especially twisted history; the other may help with future tweaks to the zipper algorithm. |
||
| #1 | 6297 | Sam Stafford |
Work so far on "deep annotate". Been getting a lot of questions on this lately from other people working on the same thing; might as well pool efforts. |