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: section_makecomposite * Purpose: * Make a composite list of sections by traversing a list of sections. * Return a handle to a list of sections. * During this, set state, leftbase and rightbase for sections. * Comments: * This function creates a list that corresponds to the 'best' view * of the differences between the two lists. We place sections from the * two lists into one composite list. Sections that match each other are only * inserted once (from the right list). Sections that match, but in different * positions in the two lists are inserted twice, once in each position, with * status to indicate this. Unmatched sections are inserted in the correct * position. * - Take sections from the left list until the section is linked to one not * already taken. * - Then take sections from right until we find a section linked to one not * already taken. * - If the two sections waiting are linked to each other, take them both * (once- we take the right one and advance past both). * - Now we have to decide which to take in place and which to declare * 'moved'. Consider the case where the only change is that the first line * has been moved to the end. We should take the first line (as a move), * then the bulk of the file (SAME) then the last line (as a move). Hence, * in difficult cases, we take the smaller section first, to ensure that * the larger section is taken as SAME. * To indicate which section has been output, we set the state field * to STATE.MARKED once we have taken it. States in left and right * lists are of no further interest once we have built the composite. * Up to this point we have worked off the STATE of a section. By now * all the section links are in place, so we can use them too. */ public static void MakeComposite(ListAnchor compo,ListAnchor secsleft, ListAnchor secsright) { Section left, right; compo.RemoveAll(); left = (Section)secsleft.GetHead(); right = (Section)secsright.GetHead(); while ( (left != null) || (right != null)) { if (left == null) { /* no more in left list - take right section is it moved or just unmatched ? */ if( right.link == null ){ TakeSection( compo, null, right, STATE.RIGHTONLY ); right = (Section)right.GetNext(); } else { TakeSection( compo, right.link, right, STATE.MOVEDRIGHT ); right = (Section)right.GetNext(); } } else if (right == null) { /* right list empty - must be left next is it moved or just unmatched ? */ if (left.link == null) { TakeSection(compo, left, null, STATE.LEFTONLY); left = (Section)left.GetNext(); } else { TakeSection(compo, left, left.link, STATE.MOVEDLEFT); left = (Section)left.GetNext(); } } else if(left.state == STATE.LEFTONLY) { /* unlinked section on left */ TakeSection(compo, left, null, STATE.LEFTONLY); left = (Section)left.GetNext(); } else if ( left.link == null ) { /* This is an ignorable blank section on the left. We ignore it. (We will take any such from the right) */ left = (Section)left.GetNext(); } else if (left.link.state==STATE.MARKED) { /* left is linked to section that is already taken*/ TakeSection(compo, left, left.link, STATE.MOVEDLEFT); left = (Section)left.GetNext(); } else if (right.link == null) { /* take unlinked section on right Either unmatched or ignorable blanks */ TakeSection(compo, null, right, right.state); right = (Section)right.GetNext(); } else if (right.link.state==STATE.MARKED) { /* right is linked to section that's already taken */ TakeSection(compo, right.link, right, STATE.MOVEDRIGHT); right = (Section)right.GetNext(); } else if (left.link == right) { /* sections match */ TakeSection(compo, left, right, STATE.SAME); right = (Section)right.GetNext(); left = (Section)left.GetNext(); } else { /* both sections linked to forward sections * decide first based on size of sections * - smallest first as a move so that largest * is an unchanged. */ if ( right.GetLineCount() > left.GetLineCount() ){ TakeSection(compo, left, left.link, STATE.MOVEDLEFT); left = (Section)left.GetNext(); } else { TakeSection(compo, right.link, right, STATE.MOVEDRIGHT); right = (Section)right.GetNext(); } } } }