public STATE state; /* compare state for section */ #endregion Fields #region Constructors public Section( Line firstParam, Line lastParam ) { first = firstParam; last = lastParam; link = null; correspond = null; state = STATE.NONE; leftbase = 1; rightbase = 1; }
public void compare( ListAnchor secsComposite, ListAnchor linesLeft, ListAnchor linesRight, bool isIgnoreBlanks ) { Section wholeLeft, wholeRight; ListAnchor secsLeft = new ListAnchor(); ListAnchor secsRight = new ListAnchor(); bool bChanges; do { bChanges = false; /* we have made no changes so far this time round the loop */ /* make a section covering the whole file */ wholeLeft = new Section( (Line)linesLeft. GetHead(), (Line)linesLeft.GetTail() ); wholeRight = new Section( (Line)linesRight.GetHead(), (Line)linesRight.GetTail() ); /* link up matching unique lines between these sections */ if( wholeLeft.Match( wholeRight, isIgnoreBlanks ) ) bChanges = true; /* discard previous section lists if made */ secsLeft. RemoveAll(); secsRight.RemoveAll(); /* build new section lists for both files */ Section.MakeList( secsLeft, linesLeft, true ,isIgnoreBlanks); Section.MakeList( secsRight, linesRight, false,isIgnoreBlanks); /* match up sections - make links and corresponds between * sections. Attempts to section_match corresponding * sections that are not matched. returns true if any * further links were made */ if( Section.MatchList( secsLeft, secsRight, isIgnoreBlanks ) ) bChanges = true; /* repeat as long as we keep adding new links */ } while( bChanges ); /* all possible lines linked, and section lists made . * combine the two section lists to get a view of the * whole comparison - the composite section list. This also * sets the state of each section in the composite list. */ Section.MakeComposite( secsComposite, secsLeft, secsRight ); }
/*************************************************************************** * Function: ExpandAnchor * Purpose: * Given an anchor point (two lines that we think should match), * try to link them, and the lines above and below them for as long * as the lines can be linked (are the same, are unlinked). * Return true if we make any links. */ public static bool ExpandAnchor(Section sec1, Line line1, Section sec2, Line line2,bool isIgnoreBlanks) { /* when a line is matched we set bChanges. If we notice some * blank lines, but do NOT link any new non-blank lines, we * do NOT set bChanges. (If we did it would cause a closed * loop as they would get noticed again next time. line_link * only returns true if it is a NEW link). * At this stage we are only interested in making links, not in * the size of the section that results (that fun comes later). * therefore trailing blanks at the end of a section are not * interesting and we don't look for them. */ bool bChanges = false; /* We handle the section limits by using a sentinel which is one * past the end of the section. (If the section ends at the end * of the list then the sentinel is null). */ Line leftend = (Line)sec1.last.GetNext(); Line rightend = (Line)sec2.last.GetNext(); /* null lines shall not match */ if ((line1 == null) || (line2 == null)) return false; /* check all lines forward until fail to link (because null, * not matching, or already linked). include the passed in anchor point since this has not * yet been linked. If blanks are ignorable then skip over any number of whole * blank lines. */ Line left = line1; Line right = line2; for(;;){ if( left.Link( right, isIgnoreBlanks ) ){ bChanges = true; left = (Line)left.GetNext(); right = (Line)right.GetNext(); if( left == leftend || right == rightend ) break; } else if( isIgnoreBlanks ){ /* even though no match, maybe an ignorable blank? */ bool moved = false; moved |= AbsorbAnyBlanks( ref left, leftend, /*bMoveToNext=*/true); moved |= AbsorbAnyBlanks( ref right, rightend,/*bMoveToNext=*/true); if( !moved ) break; /* it didn't match and we didn't move on */ if( left == leftend || right == rightend ) break; } else break; } /* check all matches going backwards from anchor point but only if it was a real anchor (could have been end-of-section/end-of-file and non-matching). */ if( line1.link == null ) return bChanges; left = (Line)line1.GetPrev(); right = (Line)line2.GetPrev(); if( left == null || right == null ) return bChanges; leftend = (Line)sec1.first.GetPrev(); rightend = (Line)sec2.first.GetPrev(); for(;;){ if( left.Link(right,isIgnoreBlanks) ){ bChanges = true; left = (Line)left.GetPrev(); right = (Line)right.GetPrev(); if( left == leftend || right == rightend ) break; } else if( isIgnoreBlanks ){ /* even though no match, maybe an ignorable blank? */ bool moved = false; moved |= AbsorbAnyBlanks( ref left, leftend, /*bMoveToNext=*/false); moved |= AbsorbAnyBlanks( ref right, rightend, /*bMoveToNext=*/false); if( !moved ) break; /* it didn't match and we didn't move on */ if( left == leftend || right == rightend ) break; } else break; } return bChanges; }
public bool Match(Section sec,bool isIgnoreBlanks) { if (/*(this == null)||*/ (sec == null)) return(false); if ((first == null) || (sec.first == null)) return(false); /* ASSERT if first is non-null, so is last */ /* attempt to link the first line of each file, and * if matched, expand as long as we keep matching*/ bool bLinked = false; bLinked |= ExpandAnchor(this, first, sec, sec.first, isIgnoreBlanks ); /* attempt to link the last lines of each file and * expand upwards */ bLinked |= ExpandAnchor(this, last, sec, sec.last, isIgnoreBlanks ); /* build a tree of lines, indexed by the line hashcode. * a ctree will hold only the first value of any given key, but * it will keep track of the number of items inserted on this key. * thus we can keep count of the number of times this line * (or at least this hashcode) appears.*/ Tree ctleft = MakeCTree(isIgnoreBlanks); Tree ctright = sec.MakeCTree(isIgnoreBlanks); /* for each unlinked line in one list (doesnt matter which), find if * appears once only in each list. if so, link, and expand * the link to link lines before and after the matching line * as long as they continue to match.*/ for( Line line = first; line != null; line = (Line)line.GetNext() ) { if( ( line.link == null ) && ( ctleft .getcount( line.GetHashcode(isIgnoreBlanks) ) == 1 ) && ( ctright.getcount( line.GetHashcode(isIgnoreBlanks) ) == 1) ){ /* line appears exactly once in each list */ Line line2 = (Line)ctright.find( line.GetHashcode(isIgnoreBlanks) ).data; bLinked |= ExpandAnchor( this, line, sec, line2, isIgnoreBlanks ); } if( line == last ) break; } return bLinked; }
/*************************************************************************** * Function: TakeSection * Purpose: * Add a section to the composite list. Called from make_composites * to copy a section, add it to the composite list and set the state, * leftbase and rightbase. Note that the state could be STATE.SAME * with a null section on the left. May NOT call with STATE.SAME and * a null right section! */ public static void TakeSection(ListAnchor compo, Section left, Section right, STATE state) { Section sec = null; /* select which section is being output, and change the state to indicate it has been output */ switch( state ){ case STATE.SAME: /* both the same. we mark both as output, and * take the right one. It is possible that the * left one could be null (an ignorable blank section) */ if( left != null ) left.state = STATE.MARKED; right.state = STATE.MARKED; sec = right; break; case STATE.LEFTONLY: case STATE.MOVEDLEFT: sec = left; left.state = STATE.MARKED; break; case STATE.RIGHTONLY: case STATE.MOVEDRIGHT: sec = right; right.state = STATE.MARKED; break; } /* create a new section on the list */ Section newsec = new Section( sec.first, sec.last ); compo.AddTail( newsec ); newsec.state = state; if (left != null) newsec.leftbase = left.first.linenr; else newsec.leftbase = 0; if (right != null) newsec.rightbase = right.first.linenr; else newsec.rightbase = 0; }
/*************************************************************************** * Function: section_makelist * Purpose: * Make a list of sections by traversing a list of lines. Consecutive * linked lines that are linked to consecutive lines are put in a single * section. Blocks of unlinked lines are placed in a section. * If isIgnoreBlanks is set then we first try to link them as normal. * but if they won't link then we just skip over them and keep them * in the same section. * Left must be set true iff the list of lines is a left hand section. * Returns a handle to a list of sections */ public static void MakeList(ListAnchor sections, ListAnchor linelist, bool left,bool isIgnoreBlanks) { /* for each line in the list */ for( Line line1 = (Line)linelist.GetHead(); line1 != null; line1 = (Line)line1.GetNext() ){ /* is it linked ? */ bool matched; Line line2; if( line1.link != null || ( isIgnoreBlanks && line1.IsBlank() ) ){ line2 = FindEndOfMatched(line1,isIgnoreBlanks); matched = true; } else { line2 = FindEndOfUnmatched(line1); matched = false; } Section sect = new Section(line1, line2); /* create the section and add to list */ sections.AddTail( sect ); sect.state = ( matched ? STATE.SAME : left ? STATE.LEFTONLY : STATE.RIGHTONLY ); line1 = line2; /* advance to end of section (no-op if 1 line section) */ } }