DAZipper.cpp #2

  • //
  • guest/
  • sam_stafford/
  • deepannotate/
  • DAZipper.cpp
  • View
  • Commits
  • Open Download .zip Download (6 KB)
#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.