Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
5 : * This Source Code Form is subject to the terms of the Mozilla Public
6 : * License, v. 2.0. If a copy of the MPL was not distributed with this
7 : * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
11 : * Licensed to the Apache Software Foundation (ASF) under one or more
12 : * contributor license agreements. See the NOTICE file distributed
13 : * with this work for additional information regarding copyright
14 : * ownership. The ASF licenses this file to you under the Apache
15 : * License, Version 2.0 (the "License"); you may not use this file
16 : * except in compliance with the License. You may obtain a copy of
17 : * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 : */
19 :
20 : #include <hintids.hxx>
21 : #include <vcl/vclenum.hxx>
22 : #include <editeng/crossedoutitem.hxx>
23 : #include <editeng/colritem.hxx>
24 : #include <editeng/boxitem.hxx>
25 : #include <editeng/svxenum.hxx>
26 : #include <editeng/udlnitem.hxx>
27 : #include <swmodule.hxx>
28 : #include <doc.hxx>
29 : #include <IDocumentUndoRedo.hxx>
30 : #include <DocumentContentOperationsManager.hxx>
31 : #include <IDocumentRedlineAccess.hxx>
32 : #include <IDocumentState.hxx>
33 : #include <docary.hxx>
34 : #include <pam.hxx>
35 : #include <ndtxt.hxx>
36 : #include <redline.hxx>
37 : #include <UndoRedline.hxx>
38 : #include <section.hxx>
39 : #include <tox.hxx>
40 : #include <docsh.hxx>
41 : #include <fmtcntnt.hxx>
42 :
43 : #include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
44 : #include <com/sun/star/document/XDocumentProperties.hpp>
45 :
46 : #include <vector>
47 :
48 : #include <list>
49 : #include <cctype>
50 :
51 : #include <boost/scoped_array.hpp>
52 :
53 : using namespace ::com::sun::star;
54 :
55 : using ::std::vector;
56 :
57 : class CompareLine
58 : {
59 : public:
60 0 : CompareLine() {}
61 : virtual ~CompareLine();
62 :
63 : virtual sal_uLong GetHashValue() const = 0;
64 : virtual bool Compare( const CompareLine& rLine ) const = 0;
65 : };
66 :
67 : class CompareData
68 : {
69 : protected:
70 : SwDoc& rDoc;
71 : private:
72 : size_t* pIndex;
73 : bool* pChangedFlag;
74 :
75 : SwPaM *pInsRing, *pDelRing;
76 :
77 : static sal_uLong PrevIdx( const SwNode* pNd );
78 : static sal_uLong NextIdx( const SwNode* pNd );
79 :
80 : vector< CompareLine* > aLines;
81 : bool m_bRecordDiff;
82 :
83 : // Truncate beginning and end and add all others to the LinesArray
84 : void CheckRanges( CompareData& );
85 :
86 : virtual const SwNode& GetEndOfContent() = 0;
87 :
88 : public:
89 2 : CompareData(SwDoc& rD, bool bRecordDiff)
90 : : rDoc( rD ), pIndex( 0 ), pChangedFlag( 0 ), pInsRing(0), pDelRing(0)
91 2 : , m_bRecordDiff(bRecordDiff)
92 : {
93 2 : }
94 : virtual ~CompareData();
95 :
96 : // Are there differences?
97 : bool HasDiffs( const CompareData& rData ) const;
98 :
99 : // Triggers the comparison and creation of two documents
100 : void CompareLines( CompareData& rData );
101 : // Display the differences - calls the methods ShowInsert and ShowDelete.
102 : // These are passed the start and end line number.
103 : // Displaying the actually content is to be handled by the subclass!
104 : sal_uLong ShowDiffs( const CompareData& rData );
105 :
106 : void ShowInsert( sal_uLong nStt, sal_uLong nEnd );
107 : void ShowDelete( const CompareData& rData, sal_uLong nStt,
108 : sal_uLong nEnd, sal_uLong nInsPos );
109 : void CheckForChangesInLine( const CompareData& rData,
110 : sal_uLong& nStt, sal_uLong& nEnd,
111 : sal_uLong& nThisStt, sal_uLong& nThisEnd );
112 :
113 : // Set non-ambiguous index for a line. Same lines have the same index, even in the other CompareData!
114 : void SetIndex( size_t nLine, size_t nIndex );
115 0 : size_t GetIndex( size_t nLine ) const
116 0 : { return nLine < aLines.size() ? pIndex[ nLine ] : 0; }
117 :
118 : // Set/get of a line has changed
119 : void SetChanged( size_t nLine, bool bFlag = true );
120 0 : bool GetChanged( size_t nLine ) const
121 : {
122 0 : return (pChangedFlag && nLine < aLines.size())
123 0 : && pChangedFlag[ nLine ];
124 : }
125 :
126 18 : size_t GetLineCount() const { return aLines.size(); }
127 0 : const CompareLine* GetLine( size_t nLine ) const
128 0 : { return aLines[ nLine ]; }
129 0 : void InsertLine( CompareLine* pLine )
130 0 : { aLines.push_back( pLine ); }
131 :
132 : void SetRedlinesToDoc( bool bUseDocInfo );
133 : };
134 :
135 2 : class CompareMainText : public CompareData
136 : {
137 : public:
138 2 : CompareMainText(SwDoc &rD, bool bRecordDiff=true)
139 2 : : CompareData(rD, bRecordDiff)
140 : {
141 2 : }
142 :
143 2 : virtual const SwNode& GetEndOfContent() SAL_OVERRIDE
144 : {
145 2 : return rDoc.GetNodes().GetEndOfContent();
146 : }
147 : };
148 :
149 0 : class CompareFrameFormatText : public CompareData
150 : {
151 : const SwNodeIndex &m_rIndex;
152 : public:
153 0 : CompareFrameFormatText(SwDoc &rD, const SwNodeIndex &rIndex, bool bRecordDiff=true)
154 : : CompareData(rD, bRecordDiff)
155 0 : , m_rIndex(rIndex)
156 : {
157 0 : }
158 :
159 0 : virtual const SwNode& GetEndOfContent() SAL_OVERRIDE
160 : {
161 0 : return *m_rIndex.GetNode().EndOfSectionNode();
162 : }
163 : };
164 :
165 : class Hash
166 : {
167 : struct _HashData
168 : {
169 : sal_uLong nNext, nHash;
170 : const CompareLine* pLine;
171 :
172 1 : _HashData()
173 1 : : nNext( 0 ), nHash( 0 ), pLine(0) {}
174 : };
175 :
176 : sal_uLong* pHashArr;
177 : _HashData* pDataArr;
178 : sal_uLong nCount, nPrime;
179 :
180 : public:
181 : explicit Hash( sal_uLong nSize );
182 : ~Hash();
183 :
184 : void CalcHashValue( CompareData& rData );
185 :
186 1 : sal_uLong GetCount() const { return nCount; }
187 : };
188 :
189 : class Compare
190 : {
191 : public:
192 : class MovedData
193 : {
194 : sal_uLong* pIndex;
195 : sal_uLong* pLineNum;
196 : sal_uLong nCount;
197 :
198 : public:
199 : MovedData( CompareData& rData, sal_Char* pDiscard );
200 : ~MovedData();
201 :
202 0 : sal_uLong GetIndex( sal_uLong n ) const { return pIndex[ n ]; }
203 0 : sal_uLong GetLineNum( sal_uLong n ) const { return pLineNum[ n ]; }
204 6 : sal_uLong GetCount() const { return nCount; }
205 : };
206 :
207 : private:
208 : /// Look for the moved lines
209 : class CompareSequence
210 : {
211 : CompareData &rData1, &rData2;
212 : const MovedData &rMoved1, &rMoved2;
213 : long *pMemory, *pFDiag, *pBDiag;
214 :
215 : void Compare( sal_uLong nStt1, sal_uLong nEnd1, sal_uLong nStt2, sal_uLong nEnd2 );
216 : sal_uLong CheckDiag( sal_uLong nStt1, sal_uLong nEnd1,
217 : sal_uLong nStt2, sal_uLong nEnd2, sal_uLong* pCost );
218 : public:
219 : CompareSequence( CompareData& rData1, CompareData& rData2,
220 : const MovedData& rD1, const MovedData& rD2 );
221 : ~CompareSequence();
222 : };
223 :
224 : static void CountDifference( const CompareData& rData, sal_uLong* pCounts );
225 : static void SetDiscard( const CompareData& rData,
226 : sal_Char* pDiscard, sal_uLong* pCounts );
227 : static void CheckDiscard( sal_uLong nLen, sal_Char* pDiscard );
228 : static void ShiftBoundaries( CompareData& rData1, CompareData& rData2 );
229 :
230 : public:
231 : Compare( sal_uLong nDiff, CompareData& rData1, CompareData& rData2 );
232 : };
233 :
234 0 : class ArrayComparator
235 : {
236 : public:
237 : virtual bool Compare( int nIdx1, int nIdx2 ) const = 0;
238 : virtual int GetLen1() const = 0;
239 : virtual int GetLen2() const = 0;
240 0 : virtual ~ArrayComparator() {}
241 : };
242 :
243 : /// Consider two lines equal if similar enough (e.g. look like different
244 : /// versions of the same paragraph)
245 0 : class LineArrayComparator : public ArrayComparator
246 : {
247 : private:
248 : int nLen1, nLen2;
249 : const CompareData &rData1, &rData2;
250 : int nFirst1, nFirst2;
251 :
252 : public:
253 : LineArrayComparator( const CompareData &rD1, const CompareData &rD2,
254 : int nStt1, int nEnd1, int nStt2, int nEnd2 );
255 :
256 : virtual bool Compare( int nIdx1, int nIdx2 ) const SAL_OVERRIDE;
257 0 : virtual int GetLen1() const SAL_OVERRIDE { return nLen1; }
258 0 : virtual int GetLen2() const SAL_OVERRIDE { return nLen2; }
259 : };
260 :
261 : class WordArrayComparator : public ArrayComparator
262 : {
263 : private:
264 : const SwTextNode *pTextNd1, *pTextNd2;
265 : int *pPos1, *pPos2;
266 : int nCnt1, nCnt2; // number of words
267 :
268 : static void CalcPositions( int *pPos, const SwTextNode *pTextNd, int &nCnt );
269 :
270 : public:
271 : WordArrayComparator( const SwTextNode *pNode1, const SwTextNode *pNode2 );
272 : virtual ~WordArrayComparator();
273 :
274 : virtual bool Compare( int nIdx1, int nIdx2 ) const SAL_OVERRIDE;
275 0 : virtual int GetLen1() const SAL_OVERRIDE { return nCnt1; }
276 0 : virtual int GetLen2() const SAL_OVERRIDE { return nCnt2; }
277 : int GetCharSequence( const int *pWordLcs1, const int *pWordLcs2,
278 : int *pSubseq1, int *pSubseq2, int nLcsLen );
279 : };
280 :
281 0 : class CharArrayComparator : public ArrayComparator
282 : {
283 : private:
284 : const SwTextNode *pTextNd1, *pTextNd2;
285 :
286 : public:
287 0 : CharArrayComparator( const SwTextNode *pNode1, const SwTextNode *pNode2 )
288 0 : : pTextNd1( pNode1 ), pTextNd2( pNode2 )
289 : {
290 0 : }
291 :
292 : virtual bool Compare( int nIdx1, int nIdx2 ) const SAL_OVERRIDE;
293 0 : virtual int GetLen1() const SAL_OVERRIDE { return pTextNd1->GetText().getLength(); }
294 0 : virtual int GetLen2() const SAL_OVERRIDE { return pTextNd2->GetText().getLength(); }
295 : };
296 :
297 : /// Options set in Tools->Options->Writer->Comparison
298 : struct CmpOptionsContainer
299 : {
300 : SvxCompareMode eCmpMode;
301 : int nIgnoreLen;
302 : bool bUseRsid;
303 : } CmpOptions;
304 :
305 : class CommonSubseq
306 : {
307 : private:
308 : int *pData;
309 : int nSize;
310 :
311 : protected:
312 : ArrayComparator &rCmp;
313 :
314 0 : CommonSubseq( ArrayComparator &rComparator, int nMaxSize )
315 0 : : nSize( nMaxSize ), rCmp( rComparator )
316 : {
317 0 : pData = new int[ nSize ];
318 0 : }
319 :
320 0 : ~CommonSubseq()
321 : {
322 0 : delete[] pData;
323 0 : }
324 :
325 : int FindLCS( int *pLcs1 = 0, int *pLcs2 = 0, int nStt1 = 0,
326 : int nEnd1 = 0, int nStt2 = 0, int nEnd2 = 0 );
327 :
328 : public:
329 : static int IgnoreIsolatedPieces( int *pLcs1, int *pLcs2, int nLen1, int nLen2,
330 : int nLcsLen, int nPieceLen );
331 : };
332 :
333 : /// Use Hirschberg's algorithm to find LCS in linear space
334 : class LgstCommonSubseq: public CommonSubseq
335 : {
336 : private:
337 : static const int CUTOFF = 1<<20; // Stop recursion at this value
338 :
339 : int *pL1, *pL2;
340 : int *pBuff1, *pBuff2;
341 :
342 : void FindL( int *pL, int nStt1, int nEnd1, int nStt2, int nEnd2 );
343 : int HirschbergLCS( int *pLcs1, int *pLcs2, int nStt1, int nEnd1,
344 : int nStt2, int nEnd2 );
345 :
346 : public:
347 : explicit LgstCommonSubseq( ArrayComparator &rComparator );
348 : ~LgstCommonSubseq();
349 :
350 : int Find( int *pSubseq1, int *pSubseq2 );
351 : };
352 :
353 : /// Find a common subsequence in linear time
354 0 : class FastCommonSubseq: private CommonSubseq
355 : {
356 : private:
357 : static const int CUTOFF = 2056;
358 :
359 : int FindFastCS( int *pSeq1, int *pSeq2, int nStt1, int nEnd1,
360 : int nStt2, int nEnd2 );
361 :
362 : public:
363 0 : explicit FastCommonSubseq( ArrayComparator &rComparator )
364 0 : : CommonSubseq( rComparator, CUTOFF )
365 : {
366 0 : }
367 :
368 0 : int Find( int *pSubseq1, int *pSubseq2 )
369 : {
370 0 : return FindFastCS( pSubseq1, pSubseq2, 0, rCmp.GetLen1(),
371 0 : 0, rCmp.GetLen2() );
372 : }
373 : };
374 :
375 0 : CompareLine::~CompareLine() {}
376 :
377 4 : CompareData::~CompareData()
378 : {
379 2 : if( pDelRing )
380 : {
381 0 : while( pDelRing->GetNext() != pDelRing )
382 0 : delete pDelRing->GetNext();
383 0 : delete pDelRing;
384 : }
385 2 : if( pInsRing )
386 : {
387 0 : while( pInsRing->GetNext() != pInsRing )
388 0 : delete pInsRing->GetNext();
389 0 : delete pInsRing;
390 : }
391 :
392 2 : delete[] pIndex;
393 2 : delete[] pChangedFlag;
394 2 : }
395 :
396 0 : void CompareData::SetIndex( size_t nLine, size_t nIndex )
397 : {
398 0 : if( !pIndex )
399 : {
400 0 : pIndex = new size_t[ aLines.size() ];
401 0 : memset( pIndex, 0, aLines.size() * sizeof( size_t ) );
402 : }
403 0 : if( nLine < aLines.size() )
404 0 : pIndex[ nLine ] = nIndex;
405 0 : }
406 :
407 0 : void CompareData::SetChanged( size_t nLine, bool bFlag )
408 : {
409 0 : if( !pChangedFlag )
410 : {
411 0 : pChangedFlag = new bool[ aLines.size() +1 ];
412 0 : memset( pChangedFlag, 0, (aLines.size() +1) * sizeof( bool ) );
413 : }
414 0 : if( nLine < aLines.size() )
415 0 : pChangedFlag[ nLine ] = bFlag;
416 0 : }
417 :
418 1 : void CompareData::CompareLines( CompareData& rData )
419 : {
420 1 : CheckRanges( rData );
421 :
422 : sal_uLong nDifferent;
423 : {
424 1 : Hash aH( GetLineCount() + rData.GetLineCount() + 1 );
425 1 : aH.CalcHashValue( *this );
426 1 : aH.CalcHashValue( rData );
427 1 : nDifferent = aH.GetCount();
428 : }
429 : {
430 1 : Compare aComp( nDifferent, *this, rData );
431 : }
432 1 : }
433 :
434 0 : sal_uLong CompareData::ShowDiffs( const CompareData& rData )
435 : {
436 0 : sal_uLong nLen1 = rData.GetLineCount(), nLen2 = GetLineCount();
437 0 : sal_uLong nStt1 = 0, nStt2 = 0;
438 0 : sal_uLong nCnt = 0;
439 :
440 0 : while( nStt1 < nLen1 || nStt2 < nLen2 )
441 : {
442 0 : if( rData.GetChanged( nStt1 ) || GetChanged( nStt2 ) )
443 : {
444 : // Find a region of different lines between two pairs of identical
445 : // lines.
446 0 : sal_uLong nSav1 = nStt1, nSav2 = nStt2;
447 0 : while( nStt1 < nLen1 && rData.GetChanged( nStt1 )) ++nStt1;
448 0 : while( nStt2 < nLen2 && GetChanged( nStt2 )) ++nStt2;
449 :
450 0 : if (m_bRecordDiff)
451 : {
452 : // Check if there are changed lines (only slightly different) and
453 : // compare them in detail.
454 0 : CheckForChangesInLine( rData, nSav1, nStt1, nSav2, nStt2 );
455 : }
456 :
457 0 : ++nCnt;
458 : }
459 0 : ++nStt1, ++nStt2;
460 : }
461 0 : return nCnt;
462 : }
463 :
464 1 : bool CompareData::HasDiffs( const CompareData& rData ) const
465 : {
466 1 : bool bRet = false;
467 1 : sal_uLong nLen1 = rData.GetLineCount(), nLen2 = GetLineCount();
468 1 : sal_uLong nStt1 = 0, nStt2 = 0;
469 :
470 2 : while( nStt1 < nLen1 || nStt2 < nLen2 )
471 : {
472 0 : if( rData.GetChanged( nStt1 ) || GetChanged( nStt2 ) )
473 : {
474 0 : bRet = true;
475 0 : break;
476 : }
477 0 : ++nStt1, ++nStt2;
478 : }
479 1 : return bRet;
480 : }
481 :
482 1 : Hash::Hash( sal_uLong nSize )
483 1 : : nCount(1)
484 : {
485 :
486 : static const sal_uLong primes[] =
487 : {
488 : 509,
489 : 1021,
490 : 2039,
491 : 4093,
492 : 8191,
493 : 16381,
494 : 32749,
495 : 65521,
496 : 131071,
497 : 262139,
498 : 524287,
499 : 1048573,
500 : 2097143,
501 : 4194301,
502 : 8388593,
503 : 16777213,
504 : 33554393,
505 : 67108859, /* Preposterously large . . . */
506 : 134217689,
507 : 268435399,
508 : 536870909,
509 : 1073741789,
510 : 2147483647,
511 : 0
512 : };
513 : int i;
514 :
515 1 : pDataArr = new _HashData[ nSize ];
516 1 : pDataArr[0].nNext = 0;
517 1 : pDataArr[0].nHash = 0,
518 1 : pDataArr[0].pLine = 0;
519 1 : nPrime = primes[0];
520 :
521 1 : for( i = 0; primes[i] < nSize / 3; i++)
522 0 : if( !primes[i] )
523 : {
524 0 : pHashArr = 0;
525 1 : return;
526 : }
527 1 : nPrime = primes[ i ];
528 1 : pHashArr = new sal_uLong[ nPrime ];
529 1 : memset( pHashArr, 0, nPrime * sizeof( sal_uLong ) );
530 : }
531 :
532 1 : Hash::~Hash()
533 : {
534 1 : delete[] pHashArr;
535 1 : delete[] pDataArr;
536 1 : }
537 :
538 2 : void Hash::CalcHashValue( CompareData& rData )
539 : {
540 2 : if( pHashArr )
541 : {
542 2 : for( size_t n = 0; n < rData.GetLineCount(); ++n )
543 : {
544 0 : const CompareLine* pLine = rData.GetLine( n );
545 : OSL_ENSURE( pLine, "where is the line?" );
546 0 : sal_uLong nH = pLine->GetHashValue();
547 :
548 0 : sal_uLong* pFound = &pHashArr[ nH % nPrime ];
549 : size_t i;
550 0 : for( i = *pFound; ; i = pDataArr[i].nNext )
551 0 : if( !i )
552 : {
553 0 : i = nCount++;
554 0 : pDataArr[i].nNext = *pFound;
555 0 : pDataArr[i].nHash = nH;
556 0 : pDataArr[i].pLine = pLine;
557 0 : *pFound = i;
558 0 : break;
559 : }
560 0 : else if( pDataArr[i].nHash == nH &&
561 0 : pDataArr[i].pLine->Compare( *pLine ))
562 0 : break;
563 :
564 0 : rData.SetIndex( n, i );
565 0 : }
566 : }
567 2 : }
568 :
569 1 : Compare::Compare( sal_uLong nDiff, CompareData& rData1, CompareData& rData2 )
570 : {
571 : MovedData *pMD1, *pMD2;
572 : // Look for the differing lines
573 : {
574 1 : sal_Char* pDiscard1 = new sal_Char[ rData1.GetLineCount() ];
575 1 : sal_Char* pDiscard2 = new sal_Char[ rData2.GetLineCount() ];
576 :
577 1 : sal_uLong* pCount1 = new sal_uLong[ nDiff ];
578 1 : sal_uLong* pCount2 = new sal_uLong[ nDiff ];
579 1 : memset( pCount1, 0, nDiff * sizeof( sal_uLong ));
580 1 : memset( pCount2, 0, nDiff * sizeof( sal_uLong ));
581 :
582 : // find indices in CompareData which have been assigned multiple times
583 1 : CountDifference( rData1, pCount1 );
584 1 : CountDifference( rData2, pCount2 );
585 :
586 : // All which occur only once now have either been inserted or deleted.
587 : // All which are also contained in the other one have been moved.
588 1 : SetDiscard( rData1, pDiscard1, pCount2 );
589 1 : SetDiscard( rData2, pDiscard2, pCount1 );
590 :
591 : // forget the arrays again
592 1 : delete [] pCount1; delete [] pCount2;
593 :
594 1 : CheckDiscard( rData1.GetLineCount(), pDiscard1 );
595 1 : CheckDiscard( rData2.GetLineCount(), pDiscard2 );
596 :
597 1 : pMD1 = new MovedData( rData1, pDiscard1 );
598 1 : pMD2 = new MovedData( rData2, pDiscard2 );
599 :
600 : // forget the arrays again
601 1 : delete [] pDiscard1; delete [] pDiscard2;
602 : }
603 :
604 : {
605 1 : CompareSequence aTmp( rData1, rData2, *pMD1, *pMD2 );
606 : }
607 :
608 1 : ShiftBoundaries( rData1, rData2 );
609 :
610 1 : delete pMD1;
611 1 : delete pMD2;
612 1 : }
613 :
614 2 : void Compare::CountDifference( const CompareData& rData, sal_uLong* pCounts )
615 : {
616 2 : sal_uLong nLen = rData.GetLineCount();
617 2 : for( sal_uLong n = 0; n < nLen; ++n )
618 : {
619 0 : sal_uLong nIdx = rData.GetIndex( n );
620 0 : ++pCounts[ nIdx ];
621 : }
622 2 : }
623 :
624 2 : void Compare::SetDiscard( const CompareData& rData,
625 : sal_Char* pDiscard, sal_uLong* pCounts )
626 : {
627 2 : const sal_uLong nLen = rData.GetLineCount();
628 :
629 : // calculate Max with respect to the line count
630 2 : sal_uLong nMax = 5;
631 :
632 4 : for( sal_uLong n = nLen / 64; ( n = n >> 2 ) > 0; )
633 0 : nMax <<= 1;
634 :
635 2 : for( sal_uLong n = 0; n < nLen; ++n )
636 : {
637 0 : sal_uLong nIdx = rData.GetIndex( n );
638 0 : if( nIdx )
639 : {
640 0 : nIdx = pCounts[ nIdx ];
641 0 : pDiscard[ n ] = !nIdx ? 1 : nIdx > nMax ? 2 : 0;
642 : }
643 : else
644 0 : pDiscard[ n ] = 0;
645 : }
646 2 : }
647 :
648 2 : void Compare::CheckDiscard( sal_uLong nLen, sal_Char* pDiscard )
649 : {
650 2 : for( sal_uLong n = 0; n < nLen; ++n )
651 : {
652 0 : if( 2 == pDiscard[ n ] )
653 0 : pDiscard[n] = 0;
654 0 : else if( pDiscard[ n ] )
655 : {
656 : sal_uLong j;
657 : sal_uLong length;
658 0 : sal_uLong provisional = 0;
659 :
660 : /* Find end of this run of discardable lines.
661 : Count how many are provisionally discardable. */
662 0 : for (j = n; j < nLen; j++)
663 : {
664 0 : if( !pDiscard[j] )
665 0 : break;
666 0 : if( 2 == pDiscard[j] )
667 0 : ++provisional;
668 : }
669 :
670 : /* Cancel provisional discards at end, and shrink the run. */
671 0 : while( j > n && 2 == pDiscard[j - 1] )
672 0 : pDiscard[ --j ] = 0, --provisional;
673 :
674 : /* Now we have the length of a run of discardable lines
675 : whose first and last are not provisional. */
676 0 : length = j - n;
677 :
678 : /* If 1/4 of the lines in the run are provisional,
679 : cancel discarding of all provisional lines in the run. */
680 0 : if (provisional * 4 > length)
681 : {
682 0 : while (j > n)
683 0 : if (pDiscard[--j] == 2)
684 0 : pDiscard[j] = 0;
685 : }
686 : else
687 : {
688 : sal_uLong consec;
689 0 : sal_uLong minimum = 1;
690 0 : sal_uLong tem = length / 4;
691 :
692 : /* MINIMUM is approximate square root of LENGTH/4.
693 : A subrun of two or more provisionals can stand
694 : when LENGTH is at least 16.
695 : A subrun of 4 or more can stand when LENGTH >= 64. */
696 0 : while ((tem = tem >> 2) > 0)
697 0 : minimum *= 2;
698 0 : minimum++;
699 :
700 : /* Cancel any subrun of MINIMUM or more provisionals
701 : within the larger run. */
702 0 : for (j = 0, consec = 0; j < length; j++)
703 0 : if (pDiscard[n + j] != 2)
704 0 : consec = 0;
705 0 : else if (minimum == ++consec)
706 : /* Back up to start of subrun, to cancel it all. */
707 0 : j -= consec;
708 0 : else if (minimum < consec)
709 0 : pDiscard[n + j] = 0;
710 :
711 : /* Scan from beginning of run
712 : until we find 3 or more nonprovisionals in a row
713 : or until the first nonprovisional at least 8 lines in.
714 : Until that point, cancel any provisionals. */
715 0 : for (j = 0, consec = 0; j < length; j++)
716 : {
717 0 : if (j >= 8 && pDiscard[n + j] == 1)
718 0 : break;
719 0 : if (pDiscard[n + j] == 2)
720 0 : consec = 0, pDiscard[n + j] = 0;
721 0 : else if (pDiscard[n + j] == 0)
722 0 : consec = 0;
723 : else
724 0 : consec++;
725 0 : if (consec == 3)
726 0 : break;
727 : }
728 :
729 : /* I advances to the last line of the run. */
730 0 : n += length - 1;
731 :
732 : /* Same thing, from end. */
733 0 : for (j = 0, consec = 0; j < length; j++)
734 : {
735 0 : if (j >= 8 && pDiscard[n - j] == 1)
736 0 : break;
737 0 : if (pDiscard[n - j] == 2)
738 0 : consec = 0, pDiscard[n - j] = 0;
739 0 : else if (pDiscard[n - j] == 0)
740 0 : consec = 0;
741 : else
742 0 : consec++;
743 0 : if (consec == 3)
744 0 : break;
745 : }
746 : }
747 : }
748 : }
749 2 : }
750 :
751 2 : Compare::MovedData::MovedData( CompareData& rData, sal_Char* pDiscard )
752 2 : : pIndex( 0 ), pLineNum( 0 ), nCount( 0 )
753 : {
754 2 : sal_uLong nLen = rData.GetLineCount();
755 : sal_uLong n;
756 :
757 2 : for( n = 0; n < nLen; ++n )
758 0 : if( pDiscard[ n ] )
759 0 : rData.SetChanged( n );
760 : else
761 0 : ++nCount;
762 :
763 2 : if( nCount )
764 : {
765 0 : pIndex = new sal_uLong[ nCount ];
766 0 : pLineNum = new sal_uLong[ nCount ];
767 :
768 0 : for( n = 0, nCount = 0; n < nLen; ++n )
769 0 : if( !pDiscard[ n ] )
770 : {
771 0 : pIndex[ nCount ] = rData.GetIndex( n );
772 0 : pLineNum[ nCount++ ] = n;
773 : }
774 : }
775 2 : }
776 :
777 2 : Compare::MovedData::~MovedData()
778 : {
779 2 : delete [] pIndex;
780 2 : delete [] pLineNum;
781 2 : }
782 :
783 : /// Find the differing lines
784 1 : Compare::CompareSequence::CompareSequence(
785 : CompareData& rD1, CompareData& rD2,
786 : const MovedData& rMD1, const MovedData& rMD2 )
787 1 : : rData1( rD1 ), rData2( rD2 ), rMoved1( rMD1 ), rMoved2( rMD2 )
788 : {
789 1 : sal_uLong nSize = rMD1.GetCount() + rMD2.GetCount() + 3;
790 1 : pMemory = new long[ nSize * 2 ];
791 1 : pFDiag = pMemory + ( rMD2.GetCount() + 1 );
792 1 : pBDiag = pMemory + ( nSize + rMD2.GetCount() + 1 );
793 :
794 1 : Compare( 0, rMD1.GetCount(), 0, rMD2.GetCount() );
795 1 : }
796 :
797 1 : Compare::CompareSequence::~CompareSequence()
798 : {
799 1 : delete [] pMemory;
800 1 : }
801 :
802 1 : void Compare::CompareSequence::Compare( sal_uLong nStt1, sal_uLong nEnd1,
803 : sal_uLong nStt2, sal_uLong nEnd2 )
804 : {
805 : /* Slide down the bottom initial diagonal. */
806 2 : while( nStt1 < nEnd1 && nStt2 < nEnd2 &&
807 0 : rMoved1.GetIndex( nStt1 ) == rMoved2.GetIndex( nStt2 ))
808 0 : ++nStt1, ++nStt2;
809 :
810 : /* Slide up the top initial diagonal. */
811 2 : while( nEnd1 > nStt1 && nEnd2 > nStt2 &&
812 0 : rMoved1.GetIndex( nEnd1 - 1 ) == rMoved2.GetIndex( nEnd2 - 1 ))
813 0 : --nEnd1, --nEnd2;
814 :
815 : /* Handle simple cases. */
816 1 : if( nStt1 == nEnd1 )
817 2 : while( nStt2 < nEnd2 )
818 0 : rData2.SetChanged( rMoved2.GetLineNum( nStt2++ ));
819 :
820 0 : else if (nStt2 == nEnd2)
821 0 : while (nStt1 < nEnd1)
822 0 : rData1.SetChanged( rMoved1.GetLineNum( nStt1++ ));
823 :
824 : else
825 : {
826 : sal_uLong c, d, b;
827 :
828 : /* Find a point of correspondence in the middle of the files. */
829 :
830 0 : d = CheckDiag( nStt1, nEnd1, nStt2, nEnd2, &c );
831 0 : b = pBDiag[ d ];
832 :
833 0 : if( 1 != c )
834 : {
835 : /* Use that point to split this problem into two subproblems. */
836 0 : Compare( nStt1, b, nStt2, b - d );
837 : /* This used to use f instead of b,
838 : but that is incorrect!
839 : It is not necessarily the case that diagonal d
840 : has a snake from b to f. */
841 0 : Compare( b, nEnd1, b - d, nEnd2 );
842 : }
843 : }
844 1 : }
845 :
846 0 : sal_uLong Compare::CompareSequence::CheckDiag( sal_uLong nStt1, sal_uLong nEnd1,
847 : sal_uLong nStt2, sal_uLong nEnd2, sal_uLong* pCost )
848 : {
849 0 : const long dmin = nStt1 - nEnd2; /* Minimum valid diagonal. */
850 0 : const long dmax = nEnd1 - nStt2; /* Maximum valid diagonal. */
851 0 : const long fmid = nStt1 - nStt2; /* Center diagonal of top-down search. */
852 0 : const long bmid = nEnd1 - nEnd2; /* Center diagonal of bottom-up search. */
853 :
854 0 : long fmin = fmid, fmax = fmid; /* Limits of top-down search. */
855 0 : long bmin = bmid, bmax = bmid; /* Limits of bottom-up search. */
856 :
857 : long c; /* Cost. */
858 0 : long odd = (fmid - bmid) & 1; /* True if southeast corner is on an odd
859 : diagonal with respect to the northwest. */
860 :
861 0 : pFDiag[fmid] = nStt1;
862 0 : pBDiag[bmid] = nEnd1;
863 :
864 0 : for (c = 1;; ++c)
865 : {
866 : long d; /* Active diagonal. */
867 :
868 : /* Extend the top-down search by an edit step in each diagonal. */
869 0 : fmin > dmin ? pFDiag[--fmin - 1] = -1 : ++fmin;
870 0 : fmax < dmax ? pFDiag[++fmax + 1] = -1 : --fmax;
871 0 : for (d = fmax; d >= fmin; d -= 2)
872 : {
873 0 : long x, y, tlo = pFDiag[d - 1], thi = pFDiag[d + 1];
874 :
875 0 : if (tlo >= thi)
876 0 : x = tlo + 1;
877 : else
878 0 : x = thi;
879 0 : y = x - d;
880 0 : while( sal_uLong(x) < nEnd1 && sal_uLong(y) < nEnd2 &&
881 0 : rMoved1.GetIndex( x ) == rMoved2.GetIndex( y ))
882 0 : ++x, ++y;
883 0 : pFDiag[d] = x;
884 0 : if( odd && bmin <= d && d <= bmax && pBDiag[d] <= pFDiag[d] )
885 : {
886 0 : *pCost = 2 * c - 1;
887 0 : return d;
888 : }
889 : }
890 :
891 : /* Similar extend the bottom-up search. */
892 0 : bmin > dmin ? pBDiag[--bmin - 1] = INT_MAX : ++bmin;
893 0 : bmax < dmax ? pBDiag[++bmax + 1] = INT_MAX : --bmax;
894 0 : for (d = bmax; d >= bmin; d -= 2)
895 : {
896 0 : long x, y, tlo = pBDiag[d - 1], thi = pBDiag[d + 1];
897 :
898 0 : if (tlo < thi)
899 0 : x = tlo;
900 : else
901 0 : x = thi - 1;
902 0 : y = x - d;
903 0 : while( sal_uLong(x) > nStt1 && sal_uLong(y) > nStt2 &&
904 0 : rMoved1.GetIndex( x - 1 ) == rMoved2.GetIndex( y - 1 ))
905 0 : --x, --y;
906 0 : pBDiag[d] = x;
907 0 : if (!odd && fmin <= d && d <= fmax && pBDiag[d] <= pFDiag[d])
908 : {
909 0 : *pCost = 2 * c;
910 0 : return d;
911 : }
912 : }
913 0 : }
914 : }
915 :
916 : namespace
917 : {
918 2 : static inline void lcl_ShiftBoundariesOneway( CompareData* const pData, CompareData* const pOtherData)
919 : {
920 2 : sal_uLong i = 0;
921 2 : sal_uLong j = 0;
922 2 : sal_uLong i_end = pData->GetLineCount();
923 2 : sal_uLong preceding = ULONG_MAX;
924 2 : sal_uLong other_preceding = ULONG_MAX;
925 :
926 : while (true)
927 : {
928 : sal_uLong start, other_start;
929 :
930 : /* Scan forwards to find beginning of another run of changes.
931 : Also keep track of the corresponding point in the other file. */
932 :
933 4 : while( i < i_end && !pData->GetChanged( i ) )
934 : {
935 0 : while( pOtherData->GetChanged( j++ ))
936 : /* Non-corresponding lines in the other file
937 : will count as the preceding batch of changes. */
938 0 : other_preceding = j;
939 0 : i++;
940 : }
941 :
942 2 : if (i == i_end)
943 2 : break;
944 :
945 0 : start = i;
946 0 : other_start = j;
947 :
948 : while (true)
949 : {
950 : /* Now find the end of this run of changes. */
951 :
952 0 : while( pData->GetChanged( ++i ))
953 : ;
954 :
955 : /* If the first changed line matches the following unchanged one,
956 : and this run does not follow right after a previous run,
957 : and there are no lines deleted from the other file here,
958 : then classify the first changed line as unchanged
959 : and the following line as changed in its place. */
960 :
961 : /* You might ask, how could this run follow right after another?
962 : Only because the previous run was shifted here. */
963 :
964 0 : if( i != i_end &&
965 0 : pData->GetIndex( start ) == pData->GetIndex( i ) &&
966 0 : !pOtherData->GetChanged( j ) &&
967 0 : !( start == preceding || other_start == other_preceding ))
968 : {
969 0 : pData->SetChanged( start++, false );
970 0 : pData->SetChanged( i );
971 : /* Since one line-that-matches is now before this run
972 : instead of after, we must advance in the other file
973 : to keep in sync. */
974 0 : ++j;
975 : }
976 : else
977 0 : break;
978 : }
979 :
980 0 : preceding = i;
981 0 : other_preceding = j;
982 0 : }
983 2 : }
984 : }
985 :
986 1 : void Compare::ShiftBoundaries( CompareData& rData1, CompareData& rData2 )
987 : {
988 1 : lcl_ShiftBoundariesOneway(&rData1, &rData2);
989 1 : lcl_ShiftBoundariesOneway(&rData2, &rData1);
990 1 : }
991 :
992 : class SwCompareLine : public CompareLine
993 : {
994 : const SwNode& rNode;
995 : public:
996 : explicit SwCompareLine( const SwNode& rNd );
997 : virtual ~SwCompareLine();
998 :
999 : virtual sal_uLong GetHashValue() const SAL_OVERRIDE;
1000 : virtual bool Compare( const CompareLine& rLine ) const SAL_OVERRIDE;
1001 :
1002 : static sal_uLong GetTextNodeHashValue( const SwTextNode& rNd, sal_uLong nVal );
1003 : static bool CompareNode( const SwNode& rDstNd, const SwNode& rSrcNd );
1004 : static bool CompareTextNd( const SwTextNode& rDstNd,
1005 : const SwTextNode& rSrcNd );
1006 :
1007 : bool ChangesInLine( const SwCompareLine& rLine,
1008 : SwPaM *& rpInsRing, SwPaM*& rpDelRing ) const;
1009 :
1010 0 : const SwNode& GetNode() const { return rNode; }
1011 :
1012 : const SwNode& GetEndNode() const;
1013 :
1014 : // for debugging
1015 : OUString GetText() const;
1016 : };
1017 :
1018 0 : SwCompareLine::SwCompareLine( const SwNode& rNd )
1019 0 : : rNode( rNd )
1020 : {
1021 0 : }
1022 :
1023 0 : SwCompareLine::~SwCompareLine()
1024 : {
1025 0 : }
1026 :
1027 0 : sal_uLong SwCompareLine::GetHashValue() const
1028 : {
1029 0 : sal_uLong nRet = 0;
1030 0 : switch( rNode.GetNodeType() )
1031 : {
1032 : case ND_TEXTNODE:
1033 0 : nRet = GetTextNodeHashValue( *rNode.GetTextNode(), nRet );
1034 0 : break;
1035 :
1036 : case ND_TABLENODE:
1037 : {
1038 0 : const SwNode* pEndNd = rNode.EndOfSectionNode();
1039 0 : SwNodeIndex aIdx( rNode );
1040 0 : while( &aIdx.GetNode() != pEndNd )
1041 : {
1042 0 : if( aIdx.GetNode().IsTextNode() )
1043 0 : nRet = GetTextNodeHashValue( *aIdx.GetNode().GetTextNode(), nRet );
1044 0 : ++aIdx;
1045 0 : }
1046 : }
1047 0 : break;
1048 :
1049 : case ND_SECTIONNODE:
1050 : {
1051 0 : OUString sStr( GetText() );
1052 0 : for( sal_Int32 n = 0; n < sStr.getLength(); ++n )
1053 0 : ( nRet <<= 1 ) += sStr[ n ];
1054 : }
1055 0 : break;
1056 :
1057 : case ND_GRFNODE:
1058 : case ND_OLENODE:
1059 : // Fixed ID? Should never occur ...
1060 0 : break;
1061 : }
1062 0 : return nRet;
1063 : }
1064 :
1065 0 : const SwNode& SwCompareLine::GetEndNode() const
1066 : {
1067 0 : const SwNode* pNd = &rNode;
1068 0 : switch( rNode.GetNodeType() )
1069 : {
1070 : case ND_TABLENODE:
1071 0 : pNd = rNode.EndOfSectionNode();
1072 0 : break;
1073 :
1074 : case ND_SECTIONNODE:
1075 : {
1076 0 : const SwSectionNode& rSNd = static_cast<const SwSectionNode&>(rNode);
1077 0 : const SwSection& rSect = rSNd.GetSection();
1078 0 : if( CONTENT_SECTION != rSect.GetType() || rSect.IsProtect() )
1079 0 : pNd = rNode.EndOfSectionNode();
1080 : }
1081 0 : break;
1082 : }
1083 0 : return *pNd;
1084 : }
1085 :
1086 0 : bool SwCompareLine::Compare( const CompareLine& rLine ) const
1087 : {
1088 0 : return CompareNode( rNode, static_cast<const SwCompareLine&>(rLine).rNode );
1089 : }
1090 :
1091 : namespace
1092 : {
1093 0 : static OUString SimpleTableToText(const SwNode &rNode)
1094 : {
1095 0 : OUStringBuffer sRet;
1096 0 : const SwNode* pEndNd = rNode.EndOfSectionNode();
1097 0 : SwNodeIndex aIdx( rNode );
1098 0 : while (&aIdx.GetNode() != pEndNd)
1099 : {
1100 0 : if (aIdx.GetNode().IsTextNode())
1101 : {
1102 0 : if (sRet.getLength())
1103 : {
1104 0 : sRet.append( '\n' );
1105 : }
1106 0 : sRet.append( aIdx.GetNode().GetTextNode()->GetExpandText() );
1107 : }
1108 0 : ++aIdx;
1109 : }
1110 0 : return sRet.makeStringAndClear();
1111 : }
1112 : }
1113 :
1114 8 : bool SwCompareLine::CompareNode( const SwNode& rDstNd, const SwNode& rSrcNd )
1115 : {
1116 8 : if( rSrcNd.GetNodeType() != rDstNd.GetNodeType() )
1117 0 : return false;
1118 :
1119 8 : bool bRet = false;
1120 :
1121 8 : switch( rDstNd.GetNodeType() )
1122 : {
1123 : case ND_TEXTNODE:
1124 8 : bRet = CompareTextNd( *rDstNd.GetTextNode(), *rSrcNd.GetTextNode() )
1125 8 : && ( !CmpOptions.bUseRsid || rDstNd.GetTextNode()->CompareParRsid( *rSrcNd.GetTextNode() ) );
1126 8 : break;
1127 :
1128 : case ND_TABLENODE:
1129 : {
1130 0 : const SwTableNode& rTSrcNd = static_cast<const SwTableNode&>(rSrcNd);
1131 0 : const SwTableNode& rTDstNd = static_cast<const SwTableNode&>(rDstNd);
1132 :
1133 0 : bRet = ( rTSrcNd.EndOfSectionIndex() - rTSrcNd.GetIndex() ) ==
1134 0 : ( rTDstNd.EndOfSectionIndex() - rTDstNd.GetIndex() );
1135 :
1136 : // --> #i107826#: compare actual table content
1137 0 : if (bRet)
1138 : {
1139 0 : bRet = (SimpleTableToText(rSrcNd) == SimpleTableToText(rDstNd));
1140 : }
1141 : }
1142 0 : break;
1143 :
1144 : case ND_SECTIONNODE:
1145 : {
1146 0 : const SwSectionNode& rSSrcNd = static_cast<const SwSectionNode&>(rSrcNd),
1147 0 : & rSDstNd = static_cast<const SwSectionNode&>(rDstNd);
1148 0 : const SwSection& rSrcSect = rSSrcNd.GetSection(),
1149 0 : & rDstSect = rSDstNd.GetSection();
1150 0 : SectionType eSrcSectType = rSrcSect.GetType(),
1151 0 : eDstSectType = rDstSect.GetType();
1152 0 : switch( eSrcSectType )
1153 : {
1154 : case CONTENT_SECTION:
1155 0 : bRet = CONTENT_SECTION == eDstSectType &&
1156 0 : rSrcSect.IsProtect() == rDstSect.IsProtect();
1157 0 : if( bRet && rSrcSect.IsProtect() )
1158 : {
1159 : // the only have they both the same size
1160 0 : bRet = ( rSSrcNd.EndOfSectionIndex() - rSSrcNd.GetIndex() ) ==
1161 0 : ( rSDstNd.EndOfSectionIndex() - rSDstNd.GetIndex() );
1162 : }
1163 0 : break;
1164 :
1165 : case TOX_HEADER_SECTION:
1166 : case TOX_CONTENT_SECTION:
1167 0 : if( TOX_HEADER_SECTION == eDstSectType ||
1168 : TOX_CONTENT_SECTION == eDstSectType )
1169 : {
1170 : // the same type of TOX?
1171 0 : const SwTOXBase* pSrcTOX = rSrcSect.GetTOXBase();
1172 0 : const SwTOXBase* pDstTOX = rDstSect.GetTOXBase();
1173 0 : bRet = pSrcTOX && pDstTOX
1174 0 : && pSrcTOX->GetType() == pDstTOX->GetType()
1175 0 : && pSrcTOX->GetTitle() == pDstTOX->GetTitle()
1176 0 : && pSrcTOX->GetTypeName() == pDstTOX->GetTypeName()
1177 0 : ;
1178 : }
1179 0 : break;
1180 :
1181 : case DDE_LINK_SECTION:
1182 : case FILE_LINK_SECTION:
1183 0 : bRet = eSrcSectType == eDstSectType &&
1184 0 : rSrcSect.GetLinkFileName() ==
1185 0 : rDstSect.GetLinkFileName();
1186 0 : break;
1187 : }
1188 : }
1189 0 : break;
1190 :
1191 : case ND_ENDNODE:
1192 0 : bRet = rSrcNd.StartOfSectionNode()->GetNodeType() ==
1193 0 : rDstNd.StartOfSectionNode()->GetNodeType();
1194 :
1195 : // --> #i107826#: compare actual table content
1196 0 : if (bRet && rSrcNd.StartOfSectionNode()->GetNodeType() == ND_TABLENODE)
1197 : {
1198 : bRet = CompareNode(
1199 0 : *rSrcNd.StartOfSectionNode(), *rDstNd.StartOfSectionNode());
1200 : }
1201 :
1202 0 : break;
1203 : }
1204 8 : return bRet;
1205 : }
1206 :
1207 0 : OUString SwCompareLine::GetText() const
1208 : {
1209 0 : OUString sRet;
1210 0 : switch( rNode.GetNodeType() )
1211 : {
1212 : case ND_TEXTNODE:
1213 0 : sRet = rNode.GetTextNode()->GetExpandText();
1214 0 : break;
1215 :
1216 : case ND_TABLENODE:
1217 : {
1218 0 : sRet = "Tabelle: " + SimpleTableToText(rNode);
1219 : }
1220 0 : break;
1221 :
1222 : case ND_SECTIONNODE:
1223 : {
1224 0 : sRet = "Section - Node:";
1225 :
1226 0 : const SwSectionNode& rSNd = static_cast<const SwSectionNode&>(rNode);
1227 0 : const SwSection& rSect = rSNd.GetSection();
1228 0 : switch( rSect.GetType() )
1229 : {
1230 : case CONTENT_SECTION:
1231 0 : if( rSect.IsProtect() )
1232 0 : sRet += OUString::number(
1233 0 : rSNd.EndOfSectionIndex() - rSNd.GetIndex() );
1234 0 : break;
1235 :
1236 : case TOX_HEADER_SECTION:
1237 : case TOX_CONTENT_SECTION:
1238 : {
1239 0 : const SwTOXBase* pTOX = rSect.GetTOXBase();
1240 0 : if( pTOX )
1241 0 : sRet += pTOX->GetTitle() + pTOX->GetTypeName() +
1242 0 : OUString::number(pTOX->GetType());
1243 : }
1244 0 : break;
1245 :
1246 : case DDE_LINK_SECTION:
1247 : case FILE_LINK_SECTION:
1248 0 : sRet += rSect.GetLinkFileName();
1249 0 : break;
1250 : }
1251 : }
1252 0 : break;
1253 :
1254 : case ND_GRFNODE:
1255 0 : sRet = "Grafik - Node:";
1256 0 : break;
1257 : case ND_OLENODE:
1258 0 : sRet = "OLE - Node:";
1259 0 : break;
1260 : }
1261 0 : return sRet;
1262 : }
1263 :
1264 0 : sal_uLong SwCompareLine::GetTextNodeHashValue( const SwTextNode& rNd, sal_uLong nVal )
1265 : {
1266 0 : OUString sStr( rNd.GetExpandText() );
1267 0 : for( sal_Int32 n = 0; n < sStr.getLength(); ++n )
1268 0 : ( nVal <<= 1 ) += sStr[ n ];
1269 0 : return nVal;
1270 : }
1271 :
1272 8 : bool SwCompareLine::CompareTextNd( const SwTextNode& rDstNd,
1273 : const SwTextNode& rSrcNd )
1274 : {
1275 8 : bool bRet = false;
1276 : // Very simple at first
1277 8 : if( rDstNd.GetText() == rSrcNd.GetText() )
1278 : {
1279 : // The text is the same, but are the "special attributes" (0xFF) also the same?
1280 8 : bRet = true;
1281 : }
1282 8 : return bRet;
1283 : }
1284 :
1285 0 : bool SwCompareLine::ChangesInLine( const SwCompareLine& rLine,
1286 : SwPaM *& rpInsRing, SwPaM*& rpDelRing ) const
1287 : {
1288 0 : bool bRet = false;
1289 :
1290 : // Only compare textnodes
1291 0 : if( ND_TEXTNODE == rNode.GetNodeType() &&
1292 0 : ND_TEXTNODE == rLine.GetNode().GetNodeType() )
1293 : {
1294 0 : SwTextNode& rDstNd = *const_cast<SwTextNode*>(rNode.GetTextNode());
1295 0 : const SwTextNode& rSrcNd = *rLine.GetNode().GetTextNode();
1296 0 : SwDoc* pDstDoc = rDstNd.GetDoc();
1297 :
1298 0 : int nLcsLen = 0;
1299 :
1300 0 : int nDstLen = rDstNd.GetText().getLength();
1301 0 : int nSrcLen = rSrcNd.GetText().getLength();
1302 :
1303 0 : int nMinLen = std::min( nDstLen , nSrcLen );
1304 0 : int nAvgLen = ( nDstLen + nSrcLen )/2;
1305 :
1306 0 : std::vector<int> aLcsDst( nMinLen + 1 );
1307 0 : std::vector<int> aLcsSrc( nMinLen + 1 );
1308 :
1309 0 : if( CmpOptions.eCmpMode == SVX_CMP_BY_WORD )
1310 : {
1311 0 : std::vector<int> aTmpLcsDst( nMinLen + 1 );
1312 0 : std::vector<int> aTmpLcsSrc( nMinLen + 1 );
1313 :
1314 0 : WordArrayComparator aCmp( &rDstNd, &rSrcNd );
1315 :
1316 0 : LgstCommonSubseq aSeq( aCmp );
1317 :
1318 0 : nLcsLen = aSeq.Find( &aTmpLcsDst[0], &aTmpLcsSrc[0] );
1319 :
1320 0 : if( CmpOptions.nIgnoreLen )
1321 : {
1322 0 : nLcsLen = CommonSubseq::IgnoreIsolatedPieces( &aTmpLcsDst[0], &aTmpLcsSrc[0],
1323 : aCmp.GetLen1(), aCmp.GetLen2(),
1324 0 : nLcsLen, CmpOptions.nIgnoreLen );
1325 : }
1326 :
1327 0 : nLcsLen = aCmp.GetCharSequence( &aTmpLcsDst[0], &aTmpLcsSrc[0],
1328 0 : &aLcsDst[0], &aLcsSrc[0], nLcsLen );
1329 : }
1330 : else
1331 : {
1332 0 : CharArrayComparator aCmp( &rDstNd, &rSrcNd );
1333 0 : LgstCommonSubseq aSeq( aCmp );
1334 :
1335 0 : nLcsLen = aSeq.Find( &aLcsDst[0], &aLcsSrc[0] );
1336 :
1337 0 : if( CmpOptions.nIgnoreLen )
1338 : {
1339 0 : nLcsLen = CommonSubseq::IgnoreIsolatedPieces( &aLcsDst[0], &aLcsSrc[0], nDstLen,
1340 : nSrcLen, nLcsLen,
1341 0 : CmpOptions.nIgnoreLen );
1342 0 : }
1343 : }
1344 :
1345 : // find the sum of the squares of the continuous substrings
1346 0 : int nSqSum = 0;
1347 0 : int nCnt = 1;
1348 0 : for( int i = 0; i < nLcsLen; i++ )
1349 : {
1350 0 : if( i != nLcsLen - 1 && aLcsDst[i] + 1 == aLcsDst[i + 1]
1351 0 : && aLcsSrc[i] + 1 == aLcsSrc[i + 1] )
1352 : {
1353 0 : nCnt++;
1354 : }
1355 : else
1356 : {
1357 0 : nSqSum += nCnt*nCnt;
1358 0 : nCnt = 1;
1359 : }
1360 : }
1361 :
1362 : // Don't compare if there aren't enough similarities
1363 0 : if ( nAvgLen >= 8 && nSqSum*32 < nAvgLen*nAvgLen )
1364 : {
1365 0 : return false;
1366 : }
1367 :
1368 : // Show the differences
1369 0 : int nSkip = 0;
1370 0 : for( int i = 0; i <= nLcsLen; i++ )
1371 : {
1372 0 : int nDstFrom = i ? (aLcsDst[i - 1] + 1) : 0;
1373 0 : int nDstTo = ( i == nLcsLen ) ? nDstLen : aLcsDst[i];
1374 0 : int nSrcFrom = i ? (aLcsSrc[i - 1] + 1) : 0;
1375 0 : int nSrcTo = ( i == nLcsLen ) ? nSrcLen : aLcsSrc[i];
1376 :
1377 0 : SwPaM aPam( rDstNd, nDstTo + nSkip );
1378 :
1379 0 : if ( nDstFrom < nDstTo )
1380 : {
1381 0 : SwPaM* pTmp = new SwPaM( *aPam.GetPoint(), rpInsRing );
1382 0 : if( !rpInsRing )
1383 0 : rpInsRing = pTmp;
1384 0 : pTmp->SetMark();
1385 0 : pTmp->GetMark()->nContent = nDstFrom + nSkip;
1386 : }
1387 :
1388 0 : if ( nSrcFrom < nSrcTo )
1389 : {
1390 0 : bool bUndo = pDstDoc->GetIDocumentUndoRedo().DoesUndo();
1391 0 : pDstDoc->GetIDocumentUndoRedo().DoUndo( false );
1392 0 : SwPaM aCpyPam( rSrcNd, nSrcFrom );
1393 0 : aCpyPam.SetMark();
1394 0 : aCpyPam.GetPoint()->nContent = nSrcTo;
1395 0 : aCpyPam.GetDoc()->getIDocumentContentOperations().CopyRange( aCpyPam, *aPam.GetPoint(),
1396 0 : /*bCopyAll=*/false, /*bCheckPos=*/true );
1397 0 : pDstDoc->GetIDocumentUndoRedo().DoUndo( bUndo );
1398 :
1399 0 : SwPaM* pTmp = new SwPaM( *aPam.GetPoint(), rpDelRing );
1400 0 : if( !rpDelRing )
1401 0 : rpDelRing = pTmp;
1402 :
1403 0 : pTmp->SetMark();
1404 0 : pTmp->GetMark()->nContent = nDstTo + nSkip;
1405 0 : nSkip += nSrcTo - nSrcFrom;
1406 :
1407 0 : if( rpInsRing )
1408 : {
1409 0 : SwPaM* pCorr = static_cast<SwPaM*>(rpInsRing->GetPrev());
1410 0 : if( *pCorr->GetPoint() == *pTmp->GetPoint() )
1411 0 : *pCorr->GetPoint() = *pTmp->GetMark();
1412 0 : }
1413 : }
1414 0 : }
1415 :
1416 0 : bRet = true;
1417 : }
1418 :
1419 0 : return bRet;
1420 : }
1421 :
1422 18 : sal_uLong CompareData::NextIdx( const SwNode* pNd )
1423 : {
1424 18 : if( pNd->IsStartNode() )
1425 : {
1426 : const SwSectionNode* pSNd;
1427 2 : if( pNd->IsTableNode() ||
1428 0 : ( 0 != (pSNd = pNd->GetSectionNode() ) &&
1429 0 : ( CONTENT_SECTION != pSNd->GetSection().GetType() ||
1430 0 : pSNd->GetSection().IsProtect() ) ) )
1431 0 : pNd = pNd->EndOfSectionNode();
1432 : }
1433 18 : return pNd->GetIndex() + 1;
1434 : }
1435 :
1436 2 : sal_uLong CompareData::PrevIdx( const SwNode* pNd )
1437 : {
1438 2 : if( pNd->IsEndNode() )
1439 : {
1440 : const SwSectionNode* pSNd;
1441 4 : if( pNd->StartOfSectionNode()->IsTableNode() ||
1442 2 : ( 0 != (pSNd = pNd->StartOfSectionNode()->GetSectionNode() ) &&
1443 0 : ( CONTENT_SECTION != pSNd->GetSection().GetType() ||
1444 0 : pSNd->GetSection().IsProtect() ) ) )
1445 0 : pNd = pNd->StartOfSectionNode();
1446 : }
1447 2 : return pNd->GetIndex() - 1;
1448 : }
1449 :
1450 1 : void CompareData::CheckRanges( CompareData& rData )
1451 : {
1452 1 : const SwNodes& rSrcNds = rData.rDoc.GetNodes();
1453 1 : const SwNodes& rDstNds = rDoc.GetNodes();
1454 :
1455 1 : const SwNode& rSrcEndNd = rData.GetEndOfContent();
1456 1 : const SwNode& rDstEndNd = GetEndOfContent();
1457 :
1458 1 : sal_uLong nSrcSttIdx = NextIdx( rSrcEndNd.StartOfSectionNode() );
1459 1 : sal_uLong nSrcEndIdx = rSrcEndNd.GetIndex();
1460 :
1461 1 : sal_uLong nDstSttIdx = NextIdx( rDstEndNd.StartOfSectionNode() );
1462 1 : sal_uLong nDstEndIdx = rDstEndNd.GetIndex();
1463 :
1464 10 : while( nSrcSttIdx < nSrcEndIdx && nDstSttIdx < nDstEndIdx )
1465 : {
1466 8 : const SwNode* pSrcNd = rSrcNds[ nSrcSttIdx ];
1467 8 : const SwNode* pDstNd = rDstNds[ nDstSttIdx ];
1468 8 : if( !SwCompareLine::CompareNode( *pSrcNd, *pDstNd ))
1469 0 : break;
1470 :
1471 8 : nSrcSttIdx = NextIdx( pSrcNd );
1472 8 : nDstSttIdx = NextIdx( pDstNd );
1473 : }
1474 :
1475 1 : nSrcEndIdx = PrevIdx( &rSrcEndNd );
1476 1 : nDstEndIdx = PrevIdx( &rDstEndNd );
1477 2 : while( nSrcSttIdx < nSrcEndIdx && nDstSttIdx < nDstEndIdx )
1478 : {
1479 0 : const SwNode* pSrcNd = rSrcNds[ nSrcEndIdx ];
1480 0 : const SwNode* pDstNd = rDstNds[ nDstEndIdx ];
1481 0 : if( !SwCompareLine::CompareNode( *pSrcNd, *pDstNd ))
1482 0 : break;
1483 :
1484 0 : nSrcEndIdx = PrevIdx( pSrcNd );
1485 0 : nDstEndIdx = PrevIdx( pDstNd );
1486 : }
1487 :
1488 2 : while( nSrcSttIdx <= nSrcEndIdx )
1489 : {
1490 0 : const SwNode* pNd = rSrcNds[ nSrcSttIdx ];
1491 0 : rData.InsertLine( new SwCompareLine( *pNd ) );
1492 0 : nSrcSttIdx = NextIdx( pNd );
1493 : }
1494 :
1495 2 : while( nDstSttIdx <= nDstEndIdx )
1496 : {
1497 0 : const SwNode* pNd = rDstNds[ nDstSttIdx ];
1498 0 : InsertLine( new SwCompareLine( *pNd ) );
1499 0 : nDstSttIdx = NextIdx( pNd );
1500 : }
1501 1 : }
1502 :
1503 0 : void CompareData::ShowInsert( sal_uLong nStt, sal_uLong nEnd )
1504 : {
1505 0 : SwPaM* pTmp = new SwPaM( static_cast<const SwCompareLine*>(GetLine( nStt ))->GetNode(), 0,
1506 0 : static_cast<const SwCompareLine*>(GetLine( nEnd-1 ))->GetEndNode(), 0,
1507 0 : pInsRing );
1508 0 : if( !pInsRing )
1509 0 : pInsRing = pTmp;
1510 :
1511 : // #i65201#: These SwPaMs are calculated smaller than needed, see comment below
1512 0 : }
1513 :
1514 0 : void CompareData::ShowDelete(
1515 : const CompareData& rData,
1516 : sal_uLong nStt,
1517 : sal_uLong nEnd,
1518 : sal_uLong nInsPos )
1519 : {
1520 : SwNodeRange aRg(
1521 0 : static_cast<const SwCompareLine*>(rData.GetLine( nStt ))->GetNode(), 0,
1522 0 : static_cast<const SwCompareLine*>(rData.GetLine( nEnd-1 ))->GetEndNode(), 1 );
1523 :
1524 0 : sal_uInt16 nOffset = 0;
1525 0 : const CompareLine* pLine = 0;
1526 0 : if( nInsPos >= 1 )
1527 : {
1528 0 : if( GetLineCount() == nInsPos )
1529 : {
1530 0 : pLine = GetLine( nInsPos-1 );
1531 0 : nOffset = 1;
1532 : }
1533 : else
1534 0 : pLine = GetLine( nInsPos );
1535 : }
1536 :
1537 : const SwNode* pLineNd;
1538 0 : if( pLine )
1539 : {
1540 0 : if( nOffset )
1541 0 : pLineNd = &static_cast<const SwCompareLine*>(pLine)->GetEndNode();
1542 : else
1543 0 : pLineNd = &static_cast<const SwCompareLine*>(pLine)->GetNode();
1544 : }
1545 : else
1546 : {
1547 0 : pLineNd = &GetEndOfContent();
1548 0 : nOffset = 0;
1549 : }
1550 :
1551 0 : SwNodeIndex aInsPos( *pLineNd, nOffset );
1552 0 : SwNodeIndex aSavePos( aInsPos, -1 );
1553 :
1554 0 : rData.rDoc.GetDocumentContentOperationsManager().CopyWithFlyInFly( aRg, 0, aInsPos );
1555 0 : rDoc.getIDocumentState().SetModified();
1556 0 : ++aSavePos;
1557 :
1558 : // #i65201#: These SwPaMs are calculated when the (old) delete-redlines are hidden,
1559 : // they will be inserted when the delete-redlines are shown again.
1560 : // To avoid unwanted insertions of delete-redlines into these new redlines, what happens
1561 : // especially at the end of the document, I reduce the SwPaM by one node.
1562 : // Before the new redlines are inserted, they have to expand again.
1563 0 : SwPaM* pTmp = new SwPaM( aSavePos.GetNode(), aInsPos.GetNode(), 0, -1, pDelRing );
1564 0 : if( !pDelRing )
1565 0 : pDelRing = pTmp;
1566 :
1567 0 : if( pInsRing )
1568 : {
1569 0 : SwPaM* pCorr = static_cast<SwPaM*>(pInsRing->GetPrev());
1570 0 : if( *pCorr->GetPoint() == *pTmp->GetPoint() )
1571 : {
1572 0 : SwNodeIndex aTmpPos( pTmp->GetMark()->nNode, -1 );
1573 0 : *pCorr->GetPoint() = SwPosition( aTmpPos );
1574 : }
1575 0 : }
1576 0 : }
1577 :
1578 0 : void CompareData::CheckForChangesInLine( const CompareData& rData,
1579 : sal_uLong& rStt, sal_uLong& rEnd,
1580 : sal_uLong& rThisStt, sal_uLong& rThisEnd )
1581 : {
1582 : LineArrayComparator aCmp( (CompareData&)*this, rData, rThisStt, rThisEnd,
1583 0 : rStt, rEnd );
1584 :
1585 0 : int nMinLen = std::min( aCmp.GetLen1(), aCmp.GetLen2() );
1586 0 : boost::scoped_array<int> pLcsDst(new int[ nMinLen ]);
1587 0 : boost::scoped_array<int> pLcsSrc(new int[ nMinLen ]);
1588 :
1589 0 : FastCommonSubseq subseq( aCmp );
1590 0 : int nLcsLen = subseq.Find( pLcsDst.get(), pLcsSrc.get() );
1591 0 : for (int i = 0; i <= nLcsLen; i++)
1592 : {
1593 : // Beginning of inserted lines (inclusive)
1594 0 : int nDstFrom = i ? pLcsDst[i - 1] + 1 : 0;
1595 : // End of inserted lines (exclusive)
1596 0 : int nDstTo = ( i == nLcsLen ) ? aCmp.GetLen1() : pLcsDst[i];
1597 : // Beginning of deleted lines (inclusive)
1598 0 : int nSrcFrom = i ? pLcsSrc[i - 1] + 1 : 0;
1599 : // End of deleted lines (exclusive)
1600 0 : int nSrcTo = ( i == nLcsLen ) ? aCmp.GetLen2() : pLcsSrc[i];
1601 :
1602 0 : if( i )
1603 : {
1604 0 : const SwCompareLine* pDstLn = static_cast<const SwCompareLine*>(GetLine( rThisStt + nDstFrom - 1 ));
1605 0 : const SwCompareLine* pSrcLn = static_cast<const SwCompareLine*>(rData.GetLine( rStt + nSrcFrom - 1 ));
1606 :
1607 : // Show differences in detail for lines that
1608 : // were matched as only slightly different
1609 0 : if( !pDstLn->ChangesInLine( *pSrcLn, pInsRing, pDelRing ) )
1610 : {
1611 0 : ShowInsert( rThisStt + nDstFrom - 1, rThisStt + nDstFrom );
1612 0 : ShowDelete( rData, rStt + nSrcFrom - 1, rStt + nSrcFrom,
1613 0 : rThisStt + nDstFrom );
1614 : }
1615 : }
1616 :
1617 : // Lines missing from source are inserted
1618 0 : if( nDstFrom != nDstTo )
1619 : {
1620 0 : ShowInsert( rThisStt + nDstFrom, rThisStt + nDstTo );
1621 : }
1622 :
1623 : // Lines missing from destination are deleted
1624 0 : if( nSrcFrom != nSrcTo )
1625 : {
1626 0 : ShowDelete( rData, rStt + nSrcFrom, rStt + nSrcTo, rThisStt + nDstTo );
1627 : }
1628 0 : }
1629 0 : }
1630 :
1631 0 : void CompareData::SetRedlinesToDoc( bool bUseDocInfo )
1632 : {
1633 0 : SwPaM* pTmp = pDelRing;
1634 :
1635 : // get the Author / TimeStamp from the "other" document info
1636 0 : sal_uInt16 nAuthor = rDoc.getIDocumentRedlineAccess().GetRedlineAuthor();
1637 0 : DateTime aTimeStamp( DateTime::SYSTEM );
1638 0 : SwDocShell *pDocShell(rDoc.GetDocShell());
1639 : OSL_ENSURE(pDocShell, "no SwDocShell");
1640 0 : if (pDocShell) {
1641 : uno::Reference<document::XDocumentPropertiesSupplier> xDPS(
1642 0 : pDocShell->GetModel(), uno::UNO_QUERY_THROW);
1643 : uno::Reference<document::XDocumentProperties> xDocProps(
1644 0 : xDPS->getDocumentProperties());
1645 : OSL_ENSURE(xDocProps.is(), "Doc has no DocumentProperties");
1646 :
1647 0 : if( bUseDocInfo && xDocProps.is() ) {
1648 0 : OUString aTmp( 1 == xDocProps->getEditingCycles()
1649 0 : ? xDocProps->getAuthor()
1650 0 : : xDocProps->getModifiedBy() );
1651 0 : util::DateTime uDT( 1 == xDocProps->getEditingCycles()
1652 0 : ? xDocProps->getCreationDate()
1653 0 : : xDocProps->getModificationDate() );
1654 :
1655 0 : if( !aTmp.isEmpty() )
1656 : {
1657 0 : nAuthor = rDoc.getIDocumentRedlineAccess().InsertRedlineAuthor( aTmp );
1658 0 : aTimeStamp = DateTime(uDT);
1659 0 : }
1660 0 : }
1661 : }
1662 :
1663 0 : if( pTmp )
1664 : {
1665 : SwRedlineData aRedlnData( nsRedlineType_t::REDLINE_DELETE, nAuthor, aTimeStamp,
1666 0 : OUString(), 0, 0 );
1667 0 : do {
1668 : // #i65201#: Expand again, see comment above.
1669 0 : if( pTmp->GetPoint()->nContent == 0 )
1670 : {
1671 0 : ++pTmp->GetPoint()->nNode;
1672 0 : pTmp->GetPoint()->nContent.Assign( pTmp->GetContentNode(), 0 );
1673 : }
1674 : // #i101009#
1675 : // prevent redlines that end on structural end node
1676 0 : if (& GetEndOfContent() ==
1677 0 : & pTmp->GetPoint()->nNode.GetNode())
1678 : {
1679 0 : --pTmp->GetPoint()->nNode;
1680 0 : SwContentNode *const pContentNode( pTmp->GetContentNode() );
1681 0 : pTmp->GetPoint()->nContent.Assign( pContentNode,
1682 0 : (pContentNode) ? pContentNode->Len() : 0 );
1683 : }
1684 :
1685 0 : rDoc.getIDocumentRedlineAccess().DeleteRedline( *pTmp, false, USHRT_MAX );
1686 :
1687 0 : if (rDoc.GetIDocumentUndoRedo().DoesUndo())
1688 : {
1689 0 : SwUndo *const pUndo(new SwUndoCompDoc( *pTmp, false )) ;
1690 0 : rDoc.GetIDocumentUndoRedo().AppendUndo(pUndo);
1691 : }
1692 0 : rDoc.getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( aRedlnData, *pTmp ), true );
1693 :
1694 0 : } while( pDelRing != ( pTmp = pTmp->GetNext()) );
1695 : }
1696 :
1697 0 : pTmp = pInsRing;
1698 0 : if( pTmp )
1699 : {
1700 0 : do {
1701 0 : if( pTmp->GetPoint()->nContent == 0 )
1702 : {
1703 0 : ++pTmp->GetPoint()->nNode;
1704 0 : pTmp->GetPoint()->nContent.Assign( pTmp->GetContentNode(), 0 );
1705 : }
1706 : // #i101009#
1707 : // prevent redlines that end on structural end node
1708 0 : if (& GetEndOfContent() ==
1709 0 : & pTmp->GetPoint()->nNode.GetNode())
1710 : {
1711 0 : --pTmp->GetPoint()->nNode;
1712 0 : SwContentNode *const pContentNode( pTmp->GetContentNode() );
1713 0 : pTmp->GetPoint()->nContent.Assign( pContentNode,
1714 0 : (pContentNode) ? pContentNode->Len() : 0 );
1715 : }
1716 0 : } while( pInsRing != ( pTmp = pTmp->GetNext()) );
1717 : SwRedlineData aRedlnData( nsRedlineType_t::REDLINE_INSERT, nAuthor, aTimeStamp,
1718 0 : OUString(), 0, 0 );
1719 :
1720 : // combine consecutive
1721 0 : if( pTmp->GetNext() != pInsRing )
1722 : {
1723 : const SwContentNode* pCNd;
1724 0 : do {
1725 0 : SwPosition& rSttEnd = *pTmp->End(),
1726 0 : & rEndStt = *(pTmp->GetNext())->Start();
1727 0 : if( rSttEnd == rEndStt ||
1728 0 : (!rEndStt.nContent.GetIndex() &&
1729 0 : rEndStt.nNode.GetIndex() - 1 == rSttEnd.nNode.GetIndex() &&
1730 0 : 0 != ( pCNd = rSttEnd.nNode.GetNode().GetContentNode() ) &&
1731 0 : rSttEnd.nContent.GetIndex() == pCNd->Len()))
1732 : {
1733 0 : if( pTmp->GetNext() == pInsRing )
1734 : {
1735 : // are consecutive, so combine
1736 0 : rEndStt = *pTmp->Start();
1737 0 : delete pTmp;
1738 0 : pTmp = pInsRing;
1739 : }
1740 : else
1741 : {
1742 : // are consecutive, so combine
1743 0 : rSttEnd = *(pTmp->GetNext())->End();
1744 0 : delete pTmp->GetNext();
1745 : }
1746 : }
1747 : else
1748 0 : pTmp = pTmp->GetNext();
1749 0 : } while( pInsRing != pTmp );
1750 : }
1751 :
1752 0 : do {
1753 0 : if( rDoc.getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( aRedlnData, *pTmp ), true) &&
1754 0 : rDoc.GetIDocumentUndoRedo().DoesUndo())
1755 : {
1756 0 : SwUndo *const pUndo(new SwUndoCompDoc( *pTmp, true ));
1757 0 : rDoc.GetIDocumentUndoRedo().AppendUndo(pUndo);
1758 : }
1759 0 : } while( pInsRing != ( pTmp = pTmp->GetNext()) );
1760 : }
1761 0 : }
1762 :
1763 : typedef std::shared_ptr<CompareData> CompareDataPtr;
1764 : typedef std::pair<CompareDataPtr, CompareDataPtr> CompareDataPtrPair;
1765 : typedef std::vector<CompareDataPtrPair> Comparators;
1766 :
1767 : namespace
1768 : {
1769 0 : Comparators buildComparators(SwDoc &rSrcDoc, SwDoc &rDestDoc)
1770 : {
1771 0 : Comparators aComparisons;
1772 : //compare main text
1773 0 : aComparisons.push_back(CompareDataPtrPair(CompareDataPtr(new CompareMainText(rSrcDoc)),
1774 0 : CompareDataPtr(new CompareMainText(rDestDoc))));
1775 :
1776 : //if we have the same number of frames then try to compare within them
1777 0 : const SwFrameFormats *pSrcFrameFormats = rSrcDoc.GetSpzFrameFormats();
1778 0 : const SwFrameFormats *pDestFrameFormats = rDestDoc.GetSpzFrameFormats();
1779 0 : if (pSrcFrameFormats->size() == pDestFrameFormats->size())
1780 : {
1781 0 : for (size_t i = 0; i < pSrcFrameFormats->size(); ++i)
1782 : {
1783 0 : const SwFrameFormat& rSrcFormat = *(*pSrcFrameFormats)[i];
1784 0 : const SwFrameFormat& rDestFormat = *(*pDestFrameFormats)[i];
1785 0 : const SwNodeIndex* pSrcIdx = rSrcFormat.GetContent().GetContentIdx();
1786 0 : const SwNodeIndex* pDestIdx = rDestFormat.GetContent().GetContentIdx();
1787 0 : if (!pSrcIdx && !pDestIdx)
1788 0 : continue;
1789 0 : if (!pSrcIdx || !pDestIdx)
1790 : break;
1791 0 : const SwNode* pSrcNode = pSrcIdx->GetNode().EndOfSectionNode();
1792 0 : const SwNode* pDestNode = pDestIdx->GetNode().EndOfSectionNode();
1793 0 : if (!pSrcNode && !pDestNode)
1794 0 : continue;
1795 0 : if (!pSrcNode || !pDestNode)
1796 : break;
1797 0 : aComparisons.push_back(CompareDataPtrPair(CompareDataPtr(new CompareFrameFormatText(rSrcDoc, *pSrcIdx)),
1798 0 : CompareDataPtr(new CompareFrameFormatText(rDestDoc, *pDestIdx))));
1799 : }
1800 : }
1801 0 : return aComparisons;
1802 : }
1803 : }
1804 :
1805 : // Returns (the difference count?) if something is different
1806 0 : long SwDoc::CompareDoc( const SwDoc& rDoc )
1807 : {
1808 0 : if( &rDoc == this )
1809 0 : return 0;
1810 :
1811 0 : long nRet = 0;
1812 :
1813 : // Get comparison options
1814 0 : CmpOptions.eCmpMode = SW_MOD()->GetCompareMode();
1815 0 : if( CmpOptions.eCmpMode == SVX_CMP_AUTO )
1816 : {
1817 0 : if( getRsidRoot() == rDoc.getRsidRoot() )
1818 : {
1819 0 : CmpOptions.eCmpMode = SVX_CMP_BY_CHAR;
1820 0 : CmpOptions.bUseRsid = true;
1821 0 : CmpOptions.nIgnoreLen = 2;
1822 : }
1823 : else
1824 : {
1825 0 : CmpOptions.eCmpMode = SVX_CMP_BY_WORD;
1826 0 : CmpOptions.bUseRsid = false;
1827 0 : CmpOptions.nIgnoreLen = 3;
1828 : }
1829 : }
1830 : else
1831 : {
1832 0 : CmpOptions.bUseRsid = getRsidRoot() == rDoc.getRsidRoot() && SW_MOD()->IsUseRsid();
1833 0 : CmpOptions.nIgnoreLen = SW_MOD()->IsIgnorePieces() ? SW_MOD()->GetPieceLen() : 0;
1834 : }
1835 :
1836 0 : GetIDocumentUndoRedo().StartUndo(UNDO_EMPTY, NULL);
1837 0 : bool bDocWasModified = getIDocumentState().IsModified();
1838 0 : SwDoc& rSrcDoc = (SwDoc&)rDoc;
1839 0 : bool bSrcModified = rSrcDoc.getIDocumentState().IsModified();
1840 :
1841 0 : RedlineMode_t eSrcRedlMode = rSrcDoc.getIDocumentRedlineAccess().GetRedlineMode();
1842 0 : rSrcDoc.getIDocumentRedlineAccess().SetRedlineMode( nsRedlineMode_t::REDLINE_SHOW_INSERT );
1843 0 : getIDocumentRedlineAccess().SetRedlineMode((RedlineMode_t)(nsRedlineMode_t::REDLINE_ON | nsRedlineMode_t::REDLINE_SHOW_INSERT));
1844 :
1845 0 : Comparators aComparisons(buildComparators(rSrcDoc, *this));
1846 :
1847 0 : for (auto& a : aComparisons)
1848 : {
1849 0 : CompareData& rD0 = *a.first.get();
1850 0 : CompareData& rD1 = *a.second.get();
1851 0 : rD1.CompareLines( rD0 );
1852 0 : nRet |= rD1.ShowDiffs( rD0 );
1853 : }
1854 :
1855 0 : if( nRet )
1856 : {
1857 0 : getIDocumentRedlineAccess().SetRedlineMode((RedlineMode_t)(nsRedlineMode_t::REDLINE_ON |
1858 0 : nsRedlineMode_t::REDLINE_SHOW_INSERT | nsRedlineMode_t::REDLINE_SHOW_DELETE));
1859 :
1860 0 : for (auto& a : aComparisons)
1861 : {
1862 0 : CompareData& rD1 = *a.second.get();
1863 0 : rD1.SetRedlinesToDoc( !bDocWasModified );
1864 : }
1865 0 : getIDocumentState().SetModified();
1866 : }
1867 :
1868 0 : rSrcDoc.getIDocumentRedlineAccess().SetRedlineMode( eSrcRedlMode );
1869 0 : getIDocumentRedlineAccess().SetRedlineMode((RedlineMode_t)(nsRedlineMode_t::REDLINE_SHOW_INSERT | nsRedlineMode_t::REDLINE_SHOW_DELETE));
1870 :
1871 0 : if( !bSrcModified )
1872 0 : rSrcDoc.getIDocumentState().ResetModified();
1873 :
1874 0 : GetIDocumentUndoRedo().EndUndo(UNDO_EMPTY, NULL);
1875 :
1876 0 : return nRet;
1877 : }
1878 :
1879 : namespace
1880 : {
1881 : struct SaveMergeRedline
1882 : {
1883 : const SwRangeRedline* pSrcRedl;
1884 : SwRangeRedline* pDestRedl;
1885 : SaveMergeRedline( const SwNode& rDstNd, const SwRangeRedline& rSrcRedl);
1886 : sal_uInt16 InsertRedline(SwPaM* pLastDestRedline);
1887 : };
1888 : }
1889 :
1890 6 : SaveMergeRedline::SaveMergeRedline( const SwNode& rDstNd,
1891 : const SwRangeRedline& rSrcRedl)
1892 6 : : pSrcRedl( &rSrcRedl )
1893 : {
1894 6 : SwPosition aPos( rDstNd );
1895 :
1896 6 : const SwPosition* pStt = rSrcRedl.Start();
1897 6 : if( rDstNd.IsContentNode() )
1898 6 : aPos.nContent.Assign( const_cast<SwContentNode*>(static_cast<const SwContentNode*>(&rDstNd)), pStt->nContent.GetIndex() );
1899 6 : pDestRedl = new SwRangeRedline( rSrcRedl.GetRedlineData(), aPos );
1900 :
1901 6 : if( nsRedlineType_t::REDLINE_DELETE == pDestRedl->GetType() )
1902 : {
1903 : // mark the area as deleted
1904 3 : const SwPosition* pEnd = pStt == rSrcRedl.GetPoint()
1905 0 : ? rSrcRedl.GetMark()
1906 3 : : rSrcRedl.GetPoint();
1907 :
1908 3 : pDestRedl->SetMark();
1909 6 : pDestRedl->GetPoint()->nNode += pEnd->nNode.GetIndex() -
1910 6 : pStt->nNode.GetIndex();
1911 6 : pDestRedl->GetPoint()->nContent.Assign( pDestRedl->GetContentNode(),
1912 9 : pEnd->nContent.GetIndex() );
1913 6 : }
1914 6 : }
1915 :
1916 6 : sal_uInt16 SaveMergeRedline::InsertRedline(SwPaM* pLastDestRedline)
1917 : {
1918 6 : sal_uInt16 nIns = 0;
1919 6 : SwDoc* pDoc = pDestRedl->GetDoc();
1920 :
1921 6 : if( nsRedlineType_t::REDLINE_INSERT == pDestRedl->GetType() )
1922 : {
1923 : // the part was inserted so copy it from the SourceDoc
1924 3 : ::sw::UndoGuard const undoGuard(pDoc->GetIDocumentUndoRedo());
1925 :
1926 6 : SwNodeIndex aSaveNd( pDestRedl->GetPoint()->nNode, -1 );
1927 3 : const sal_Int32 nSaveCnt = pDestRedl->GetPoint()->nContent.GetIndex();
1928 :
1929 3 : RedlineMode_t eOld = pDoc->getIDocumentRedlineAccess().GetRedlineMode();
1930 3 : pDoc->getIDocumentRedlineAccess().SetRedlineMode_intern((RedlineMode_t)(eOld | nsRedlineMode_t::REDLINE_IGNORE));
1931 :
1932 3 : pSrcRedl->GetDoc()->getIDocumentContentOperations().CopyRange(
1933 : *const_cast<SwPaM*>(static_cast<const SwPaM*>(pSrcRedl)),
1934 3 : *pDestRedl->GetPoint(), /*bCopyAll=*/false, /*bCheckPos=*/true );
1935 :
1936 3 : pDoc->getIDocumentRedlineAccess().SetRedlineMode_intern( eOld );
1937 :
1938 3 : pDestRedl->SetMark();
1939 3 : ++aSaveNd;
1940 3 : pDestRedl->GetMark()->nNode = aSaveNd;
1941 6 : pDestRedl->GetMark()->nContent.Assign( aSaveNd.GetNode().GetContentNode(),
1942 6 : nSaveCnt );
1943 :
1944 3 : if( pLastDestRedline && *pLastDestRedline->GetPoint() == *pDestRedl->GetPoint() )
1945 3 : *pLastDestRedline->GetPoint() = *pDestRedl->GetMark();
1946 : }
1947 : else
1948 : {
1949 : //JP 21.09.98: Bug 55909
1950 : // If there already is a deleted or inserted one at the same position, we have to split it!
1951 3 : SwPosition* pDStt = pDestRedl->GetMark(),
1952 3 : * pDEnd = pDestRedl->GetPoint();
1953 3 : sal_uInt16 n = 0;
1954 :
1955 : // find the first redline for StartPos
1956 3 : if( !pDoc->getIDocumentRedlineAccess().GetRedline( *pDStt, &n ) && n )
1957 2 : --n;
1958 :
1959 3 : const SwRedlineTable& rRedlineTable = pDoc->getIDocumentRedlineAccess().GetRedlineTable();
1960 17 : for( ; n < rRedlineTable.size(); ++n )
1961 : {
1962 14 : SwRangeRedline* pRedl = rRedlineTable[ n ];
1963 14 : SwPosition* pRStt = pRedl->Start(),
1964 14 : * pREnd = pRStt == pRedl->GetPoint() ? pRedl->GetMark()
1965 14 : : pRedl->GetPoint();
1966 23 : if( nsRedlineType_t::REDLINE_DELETE == pRedl->GetType() ||
1967 9 : nsRedlineType_t::REDLINE_INSERT == pRedl->GetType() )
1968 : {
1969 14 : SwComparePosition eCmpPos = ComparePosition( *pDStt, *pDEnd, *pRStt, *pREnd );
1970 14 : switch( eCmpPos )
1971 : {
1972 : case POS_COLLIDE_START:
1973 : case POS_BEHIND:
1974 10 : break;
1975 :
1976 : case POS_INSIDE:
1977 : case POS_EQUAL:
1978 1 : delete pDestRedl, pDestRedl = 0;
1979 : // break; -> no break !!!!
1980 :
1981 : case POS_COLLIDE_END:
1982 : case POS_BEFORE:
1983 3 : n = rRedlineTable.size();
1984 3 : break;
1985 :
1986 : case POS_OUTSIDE:
1987 : assert(pDestRedl && "is this actually impossible");
1988 1 : if (pDestRedl)
1989 : {
1990 : SwRangeRedline* pCpyRedl = new SwRangeRedline(
1991 1 : pDestRedl->GetRedlineData(), *pDStt );
1992 1 : pCpyRedl->SetMark();
1993 1 : *pCpyRedl->GetPoint() = *pRStt;
1994 :
1995 : SwUndoCompDoc *const pUndo =
1996 1 : (pDoc->GetIDocumentUndoRedo().DoesUndo())
1997 1 : ? new SwUndoCompDoc( *pCpyRedl ) : 0;
1998 :
1999 : // now modify doc: append redline, undo (and count)
2000 1 : pDoc->getIDocumentRedlineAccess().AppendRedline( pCpyRedl, true );
2001 1 : if( pUndo )
2002 : {
2003 1 : pDoc->GetIDocumentUndoRedo().AppendUndo(pUndo);
2004 : }
2005 1 : ++nIns;
2006 :
2007 1 : *pDStt = *pREnd;
2008 :
2009 : // we should start over now
2010 1 : n = USHRT_MAX;
2011 : }
2012 1 : break;
2013 :
2014 : case POS_OVERLAP_BEFORE:
2015 0 : *pDEnd = *pRStt;
2016 0 : break;
2017 :
2018 : case POS_OVERLAP_BEHIND:
2019 0 : *pDStt = *pREnd;
2020 0 : break;
2021 : }
2022 : }
2023 0 : else if( *pDEnd <= *pRStt )
2024 0 : break;
2025 : }
2026 :
2027 : }
2028 :
2029 6 : if( pDestRedl )
2030 : {
2031 5 : SwUndoCompDoc *const pUndo = (pDoc->GetIDocumentUndoRedo().DoesUndo())
2032 5 : ? new SwUndoCompDoc( *pDestRedl ) : 0;
2033 :
2034 : // now modify doc: append redline, undo (and count)
2035 5 : bool bRedlineAccepted = pDoc->getIDocumentRedlineAccess().AppendRedline( pDestRedl, true );
2036 5 : if( pUndo )
2037 : {
2038 5 : pDoc->GetIDocumentUndoRedo().AppendUndo( pUndo );
2039 : }
2040 5 : ++nIns;
2041 :
2042 : // if AppendRedline has deleted our redline, we may not keep a
2043 : // reference to it
2044 5 : if( ! bRedlineAccepted )
2045 1 : pDestRedl = NULL;
2046 : }
2047 6 : return nIns;
2048 : }
2049 :
2050 : /// Merge two documents
2051 1 : long SwDoc::MergeDoc( const SwDoc& rDoc )
2052 : {
2053 1 : if( &rDoc == this )
2054 0 : return 0;
2055 :
2056 1 : long nRet = 0;
2057 :
2058 1 : GetIDocumentUndoRedo().StartUndo(UNDO_EMPTY, NULL);
2059 :
2060 1 : SwDoc& rSrcDoc = (SwDoc&)rDoc;
2061 1 : bool bSrcModified = rSrcDoc.getIDocumentState().IsModified();
2062 :
2063 1 : RedlineMode_t eSrcRedlMode = rSrcDoc.getIDocumentRedlineAccess().GetRedlineMode();
2064 1 : rSrcDoc.getIDocumentRedlineAccess().SetRedlineMode( nsRedlineMode_t::REDLINE_SHOW_DELETE );
2065 1 : getIDocumentRedlineAccess().SetRedlineMode( nsRedlineMode_t::REDLINE_SHOW_DELETE );
2066 :
2067 1 : CompareMainText aD0(rSrcDoc, false);
2068 2 : CompareMainText aD1(*this, false);
2069 1 : aD1.CompareLines( aD0 );
2070 1 : if( !aD1.HasDiffs( aD0 ) )
2071 : {
2072 : // we want to get all redlines from the SourceDoc
2073 :
2074 : // look for all insert redlines from the SourceDoc and determine their position in the DestDoc
2075 1 : std::list<SaveMergeRedline> vRedlines;
2076 1 : const SwRedlineTable& rSrcRedlTable = rSrcDoc.getIDocumentRedlineAccess().GetRedlineTable();
2077 1 : sal_uLong nEndOfExtra = rSrcDoc.GetNodes().GetEndOfExtras().GetIndex();
2078 1 : sal_uLong nMyEndOfExtra = GetNodes().GetEndOfExtras().GetIndex();
2079 7 : for( SwRedlineTable::size_type n = 0; n < rSrcRedlTable.size(); ++n )
2080 : {
2081 6 : const SwRangeRedline* pRedl = rSrcRedlTable[ n ];
2082 6 : sal_uLong nNd = pRedl->GetPoint()->nNode.GetIndex();
2083 6 : RedlineType_t eType = pRedl->GetType();
2084 6 : if( nEndOfExtra < nNd &&
2085 3 : ( nsRedlineType_t::REDLINE_INSERT == eType || nsRedlineType_t::REDLINE_DELETE == eType ))
2086 : {
2087 6 : const SwNode* pDstNd = GetNodes()[
2088 12 : nMyEndOfExtra + nNd - nEndOfExtra ];
2089 :
2090 : // Found the position.
2091 : // Then we also have to insert the redline to the line in the DestDoc.
2092 6 : vRedlines.push_back(SaveMergeRedline(*pDstNd, *pRedl));
2093 : }
2094 : }
2095 :
2096 1 : if( !vRedlines.empty() )
2097 : {
2098 : // Carry over all into DestDoc
2099 1 : rSrcDoc.getIDocumentRedlineAccess().SetRedlineMode((RedlineMode_t)(nsRedlineMode_t::REDLINE_SHOW_INSERT | nsRedlineMode_t::REDLINE_SHOW_DELETE));
2100 :
2101 1 : getIDocumentRedlineAccess().SetRedlineMode((RedlineMode_t)(
2102 : nsRedlineMode_t::REDLINE_ON |
2103 : nsRedlineMode_t::REDLINE_SHOW_INSERT |
2104 1 : nsRedlineMode_t::REDLINE_SHOW_DELETE));
2105 :
2106 1 : SwPaM* pLastDestRedline(nullptr);
2107 7 : for(SaveMergeRedline& rRedline: vRedlines)
2108 : {
2109 6 : nRet += rRedline.InsertRedline(pLastDestRedline);
2110 6 : pLastDestRedline = rRedline.pDestRedl;
2111 : }
2112 1 : }
2113 : }
2114 :
2115 1 : rSrcDoc.getIDocumentRedlineAccess().SetRedlineMode( eSrcRedlMode );
2116 1 : if( !bSrcModified )
2117 1 : rSrcDoc.getIDocumentState().ResetModified();
2118 :
2119 1 : getIDocumentRedlineAccess().SetRedlineMode((RedlineMode_t)(nsRedlineMode_t::REDLINE_SHOW_INSERT | nsRedlineMode_t::REDLINE_SHOW_DELETE));
2120 :
2121 1 : GetIDocumentUndoRedo().EndUndo(UNDO_EMPTY, NULL);
2122 :
2123 2 : return nRet;
2124 : }
2125 :
2126 0 : LineArrayComparator::LineArrayComparator( const CompareData &rD1,
2127 : const CompareData &rD2, int nStt1,
2128 : int nEnd1, int nStt2, int nEnd2 )
2129 0 : : rData1( rD1 ), rData2( rD2 ), nFirst1( nStt1 ), nFirst2( nStt2 )
2130 : {
2131 0 : nLen1 = nEnd1 - nStt1;
2132 0 : nLen2 = nEnd2 - nStt2;
2133 0 : }
2134 :
2135 0 : bool LineArrayComparator::Compare( int nIdx1, int nIdx2 ) const
2136 : {
2137 0 : if( nIdx1 < 0 || nIdx2 < 0 || nIdx1 >= nLen1 || nIdx2 >= nLen2 )
2138 : {
2139 : OSL_ENSURE( false, "Index out of range!" );
2140 0 : return false;
2141 : }
2142 :
2143 0 : const SwTextNode *pTextNd1 = static_cast<const SwCompareLine*>( rData1.GetLine( nFirst1 + nIdx1 ) )->GetNode().GetTextNode();
2144 0 : const SwTextNode *pTextNd2 = static_cast<const SwCompareLine*>( rData2.GetLine( nFirst2 + nIdx2 ) )->GetNode().GetTextNode();
2145 :
2146 0 : if( !pTextNd1 || !pTextNd2
2147 0 : || ( CmpOptions.bUseRsid && !pTextNd1->CompareParRsid( *pTextNd2 ) ) )
2148 : {
2149 0 : return false;
2150 : }
2151 :
2152 0 : const sal_Int32 nPar1Len = pTextNd1->Len();
2153 0 : const sal_Int32 nPar2Len = pTextNd2->Len();
2154 :
2155 0 : if( std::min( nPar1Len, nPar2Len ) * 3 < std::max( nPar1Len, nPar2Len ) )
2156 : {
2157 0 : return false;
2158 : }
2159 :
2160 0 : sal_Int32 nBorderLen = ( nPar1Len + nPar2Len )/16;
2161 :
2162 0 : if( nBorderLen < 3 )
2163 : {
2164 0 : nBorderLen = std::min<sal_Int32>( 3, std::min( nPar1Len, nPar2Len ) );
2165 : }
2166 :
2167 0 : std::set<unsigned> aHashes;
2168 0 : unsigned nHash = 0;
2169 0 : unsigned nMul = 251;
2170 0 : unsigned nPow = 1;
2171 : sal_Int32 i;
2172 :
2173 0 : for( i = 0; i < nBorderLen - 1; i++ )
2174 : {
2175 0 : nPow *= nMul;
2176 : }
2177 0 : for( i = 0; i < nBorderLen; i++ )
2178 : {
2179 0 : nHash = nHash*nMul + pTextNd1->GetText()[i];
2180 : }
2181 0 : aHashes.insert( nHash );
2182 0 : for( ; i < nPar1Len; i++ )
2183 : {
2184 0 : nHash = nHash - nPow*pTextNd1->GetText()[ i - nBorderLen ];
2185 0 : nHash = nHash*nMul + pTextNd1->GetText()[ i ];
2186 :
2187 0 : aHashes.insert( nHash );
2188 : }
2189 :
2190 0 : nHash = 0;
2191 0 : for( i = 0; i < nBorderLen; i++ )
2192 : {
2193 0 : nHash = nHash*nMul + pTextNd2->GetText()[ i ];
2194 : }
2195 :
2196 0 : if( aHashes.find( nHash ) != aHashes.end() )
2197 : {
2198 0 : return true;
2199 : }
2200 :
2201 0 : for( ; i < nPar2Len; i++ )
2202 : {
2203 0 : nHash = nHash - nPow*pTextNd2->GetText()[ i - nBorderLen ];
2204 0 : nHash = nHash*nMul + pTextNd2->GetText()[ i ];
2205 0 : if( aHashes.find( nHash ) != aHashes.end() )
2206 : {
2207 0 : return true;
2208 : }
2209 : }
2210 0 : return false;
2211 : }
2212 :
2213 0 : bool CharArrayComparator::Compare( int nIdx1, int nIdx2 ) const
2214 : {
2215 0 : if( nIdx1 < 0 || nIdx2 < 0 || nIdx1 >= GetLen1() || nIdx2 >= GetLen2() )
2216 : {
2217 : OSL_ENSURE( false, "Index out of range!" );
2218 0 : return false;
2219 : }
2220 :
2221 0 : return ( !CmpOptions.bUseRsid
2222 0 : || pTextNd1->CompareRsid( *pTextNd2, nIdx1 + 1, nIdx2 + 1 ) )
2223 0 : && pTextNd1->GetText()[ nIdx1 ] == pTextNd2->GetText()[ nIdx2 ];
2224 : }
2225 :
2226 0 : WordArrayComparator::WordArrayComparator( const SwTextNode *pNode1,
2227 : const SwTextNode *pNode2 )
2228 0 : : pTextNd1( pNode1 ), pTextNd2( pNode2 )
2229 : {
2230 0 : pPos1 = new int[ pTextNd1->GetText().getLength() + 1 ];
2231 0 : pPos2 = new int[ pTextNd2->GetText().getLength() + 1 ];
2232 :
2233 0 : CalcPositions( pPos1, pTextNd1, nCnt1 );
2234 0 : CalcPositions( pPos2, pTextNd2, nCnt2 );
2235 0 : }
2236 :
2237 0 : WordArrayComparator::~WordArrayComparator()
2238 : {
2239 0 : delete[] pPos1;
2240 0 : delete[] pPos2;
2241 0 : }
2242 :
2243 0 : bool WordArrayComparator::Compare( int nIdx1, int nIdx2 ) const
2244 : {
2245 0 : int nLen = pPos1[ nIdx1 + 1 ] - pPos1[ nIdx1 ];
2246 0 : if( nLen != pPos2[ nIdx2 + 1 ] - pPos2[ nIdx2 ] )
2247 : {
2248 0 : return false;
2249 : }
2250 0 : for( int i = 0; i < nLen; i++)
2251 : {
2252 0 : if( pTextNd1->GetText()[ pPos1[ nIdx1 ] + i ]
2253 0 : != pTextNd2->GetText()[ pPos2[ nIdx2 ] + i ]
2254 0 : || ( CmpOptions.bUseRsid && !pTextNd1->CompareRsid( *pTextNd2,
2255 0 : pPos1[ nIdx1 ] + i, pPos2[ nIdx2 ] + i ) ) )
2256 : {
2257 0 : return false;
2258 : }
2259 : }
2260 0 : return true;
2261 : }
2262 :
2263 0 : int WordArrayComparator::GetCharSequence( const int *pWordLcs1,
2264 : const int *pWordLcs2, int *pSubseq1, int *pSubseq2, int nLcsLen )
2265 : {
2266 0 : int nLen = 0;
2267 0 : for( int i = 0; i < nLcsLen; i++ )
2268 : {
2269 : // Check for hash collisions
2270 0 : if( pPos1[ pWordLcs1[i] + 1 ] - pPos1[ pWordLcs1[i] ]
2271 0 : != pPos2[ pWordLcs2[i] + 1 ] - pPos2[ pWordLcs2[i] ] )
2272 : {
2273 0 : continue;
2274 : }
2275 0 : for( int j = 0; j < pPos1[pWordLcs1[i]+1] - pPos1[pWordLcs1[i]]; j++)
2276 : {
2277 0 : pSubseq1[ nLen ] = pPos1[ pWordLcs1[i] ] + j;
2278 0 : pSubseq2[ nLen ] = pPos2[ pWordLcs2[i] ] + j;
2279 :
2280 0 : if( pTextNd1->GetText()[ pPos1[ pWordLcs1[i] ] + j ]
2281 0 : != pTextNd2->GetText()[ pPos2[ pWordLcs2[i] ] + j ] )
2282 : {
2283 0 : nLen -= j;
2284 0 : break;
2285 : }
2286 :
2287 0 : nLen++;
2288 : }
2289 : }
2290 0 : return nLen;
2291 : }
2292 :
2293 0 : void WordArrayComparator::CalcPositions( int *pPos, const SwTextNode *pTextNd,
2294 : int &nCnt )
2295 : {
2296 0 : nCnt = -1;
2297 0 : for (int i = 0; i <= pTextNd->GetText().getLength(); ++i)
2298 : {
2299 0 : if (i == 0 || i == pTextNd->GetText().getLength()
2300 0 : || !isalnum( pTextNd->GetText()[ i - 1 ])
2301 0 : || !isalnum( pTextNd->GetText()[ i ]))
2302 : { // Begin new word
2303 0 : nCnt++;
2304 0 : pPos[ nCnt ] = i;
2305 : }
2306 : }
2307 0 : }
2308 :
2309 0 : int CommonSubseq::FindLCS( int *pLcs1, int *pLcs2, int nStt1, int nEnd1,
2310 : int nStt2, int nEnd2 )
2311 : {
2312 0 : int nLen1 = nEnd1 ? nEnd1 - nStt1 : rCmp.GetLen1();
2313 0 : int nLen2 = nEnd2 ? nEnd2 - nStt2 : rCmp.GetLen2();
2314 :
2315 : OSL_ASSERT( nLen1 >= 0 );
2316 : OSL_ASSERT( nLen2 >= 0 );
2317 :
2318 0 : int **pLcs = new int*[ nLen1 + 1 ];
2319 0 : pLcs[ 0 ] = pData;
2320 :
2321 0 : for( int i = 1; i < nLen1 + 1; i++ )
2322 0 : pLcs[ i ] = pLcs[ i - 1 ] + nLen2 + 1;
2323 :
2324 0 : for( int i = 0; i <= nLen1; i++ )
2325 0 : pLcs[i][0] = 0;
2326 :
2327 0 : for( int j = 0; j <= nLen2; j++ )
2328 0 : pLcs[0][j] = 0;
2329 :
2330 : // Find lcs
2331 0 : for( int i = 1; i <= nLen1; i++ )
2332 : {
2333 0 : for( int j = 1; j <= nLen2; j++ )
2334 : {
2335 0 : if( rCmp.Compare( nStt1 + i - 1, nStt2 + j - 1 ) )
2336 0 : pLcs[i][j] = pLcs[i - 1][j - 1] + 1;
2337 : else
2338 0 : pLcs[i][j] = std::max( pLcs[i][j - 1], pLcs[i - 1][j] );
2339 : }
2340 : }
2341 :
2342 0 : int nLcsLen = pLcs[ nLen1 ][ nLen2 ];
2343 :
2344 : // Recover the lcs in the two sequences
2345 0 : if( pLcs1 && pLcs2 )
2346 : {
2347 0 : int nIdx1 = nLen1;
2348 0 : int nIdx2 = nLen2;
2349 0 : int nIdx = nLcsLen - 1;
2350 :
2351 0 : while( nIdx1 > 0 && nIdx2 > 0 )
2352 : {
2353 0 : if( pLcs[ nIdx1 ][ nIdx2 ] == pLcs[ nIdx1 - 1 ][ nIdx2 ] )
2354 0 : nIdx1--;
2355 0 : else if( pLcs[ nIdx1 ][ nIdx2 ] == pLcs[ nIdx1 ][ nIdx2 - 1 ] )
2356 0 : nIdx2--;
2357 : else
2358 : {
2359 0 : nIdx1--, nIdx2--;
2360 0 : pLcs1[ nIdx ] = nIdx1 + nStt1;
2361 0 : pLcs2[ nIdx ] = nIdx2 + nStt2;
2362 0 : nIdx--;
2363 : }
2364 : }
2365 : }
2366 :
2367 0 : delete[] pLcs;
2368 :
2369 0 : return nLcsLen;
2370 : }
2371 :
2372 0 : int CommonSubseq::IgnoreIsolatedPieces( int *pLcs1, int *pLcs2, int nLen1,
2373 : int nLen2, int nLcsLen, int nPieceLen )
2374 : {
2375 0 : if( !nLcsLen )
2376 : {
2377 0 : return 0;
2378 : }
2379 :
2380 0 : int nNext = 0;
2381 :
2382 : // Don't ignore text at the beginning of the paragraphs
2383 0 : if( pLcs1[ 0 ] == 0 && pLcs2[ 0 ] == 0 )
2384 : {
2385 0 : while( nNext < nLcsLen - 1 && pLcs1[ nNext ] + 1 == pLcs1[ nNext + 1 ]
2386 0 : && pLcs2[ nNext ] + 1 == pLcs2[ nNext + 1 ] )
2387 : {
2388 0 : nNext++;
2389 : }
2390 0 : nNext++;
2391 : }
2392 :
2393 0 : int nCnt = 1;
2394 :
2395 0 : for( int i = nNext; i < nLcsLen; i++ )
2396 : {
2397 0 : if( i != nLcsLen - 1 && pLcs1[ i ] + 1 == pLcs1[ i + 1 ]
2398 0 : && pLcs2[ i ] + 1 == pLcs2[ i + 1 ] )
2399 : {
2400 0 : nCnt++;
2401 : }
2402 : else
2403 : {
2404 0 : if( nCnt > nPieceLen
2405 : // Don't ignore text at the end of the paragraphs
2406 0 : || ( i == nLcsLen - 1
2407 0 : && pLcs1[i] == nLen1 - 1 && pLcs2[i] == nLen2 - 1 ))
2408 : {
2409 0 : for( int j = i + 1 - nCnt; j <= i; j++ )
2410 : {
2411 0 : pLcs2[ nNext ] = pLcs2[ j ];
2412 0 : pLcs1[ nNext ] = pLcs1[ j ];
2413 0 : nNext++;
2414 : }
2415 : }
2416 0 : nCnt = 1;
2417 : }
2418 : }
2419 :
2420 0 : return nNext;
2421 : }
2422 :
2423 0 : LgstCommonSubseq::LgstCommonSubseq( ArrayComparator &rComparator )
2424 0 : : CommonSubseq( rComparator, CUTOFF )
2425 : {
2426 0 : pBuff1 = new int[ rComparator.GetLen2() + 1 ];
2427 0 : pBuff2 = new int[ rComparator.GetLen2() + 1 ];
2428 :
2429 0 : pL1 = new int[ rComparator.GetLen2() + 1 ];
2430 0 : pL2 = new int[ rComparator.GetLen2() + 1 ];
2431 0 : }
2432 :
2433 0 : LgstCommonSubseq::~LgstCommonSubseq()
2434 : {
2435 0 : delete[] pBuff1;
2436 0 : delete[] pBuff2;
2437 :
2438 0 : delete[] pL1;
2439 0 : delete[] pL2;
2440 0 : }
2441 :
2442 0 : void LgstCommonSubseq::FindL( int *pL, int nStt1, int nEnd1,
2443 : int nStt2, int nEnd2 )
2444 : {
2445 0 : int nLen1 = nEnd1 ? nEnd1 - nStt1 : rCmp.GetLen1();
2446 0 : int nLen2 = nEnd2 ? nEnd2 - nStt2 : rCmp.GetLen2();
2447 :
2448 0 : int *currL = pBuff1;
2449 0 : int *prevL = pBuff2;
2450 :
2451 : // Avoid memory corruption
2452 0 : if( nLen2 > rCmp.GetLen2() )
2453 : {
2454 : assert( false );
2455 0 : return;
2456 : }
2457 :
2458 0 : memset( pBuff1, 0, sizeof( *pBuff1 ) * ( nLen2 + 1 ) );
2459 0 : memset( pBuff2, 0, sizeof( *pBuff2 ) * ( nLen2 + 1 ) );
2460 :
2461 : // Find lcs
2462 0 : for( int i = 1; i <= nLen1; i++ )
2463 : {
2464 0 : for( int j = 1; j <= nLen2; j++ )
2465 : {
2466 0 : if( rCmp.Compare( nStt1 + i - 1, nStt2 + j - 1 ) )
2467 0 : currL[j] = prevL[j - 1] + 1;
2468 : else
2469 0 : currL[j] = std::max( currL[j - 1], prevL[j] );
2470 : }
2471 0 : int *tmp = currL;
2472 0 : currL = prevL;
2473 0 : prevL = tmp;
2474 : }
2475 0 : memcpy( pL, prevL, ( nLen2 + 1 ) * sizeof( *prevL ) );
2476 : }
2477 :
2478 0 : int LgstCommonSubseq::HirschbergLCS( int *pLcs1, int *pLcs2, int nStt1,
2479 : int nEnd1, int nStt2, int nEnd2 )
2480 : {
2481 : static int nLen1;
2482 : static int nLen2;
2483 0 : nLen1 = nEnd1 - nStt1;
2484 0 : nLen2 = nEnd2 - nStt2;
2485 :
2486 0 : if( ( nLen1 + 1 ) * ( nLen2 + 1 ) <= CUTOFF )
2487 : {
2488 0 : if( !nLen1 || !nLen2 )
2489 : {
2490 0 : return 0;
2491 : }
2492 0 : return FindLCS(pLcs1, pLcs2, nStt1, nEnd1, nStt2, nEnd2);
2493 : }
2494 :
2495 0 : int nMid = nLen1/2;
2496 :
2497 0 : FindL( pL1, nStt1, nStt1 + nMid, nStt2, nEnd2 );
2498 0 : FindL( pL2, nStt1 + nMid, nEnd1, nStt2, nEnd2 );
2499 :
2500 0 : int nMaxPos = 0;
2501 : static int nMaxVal;
2502 0 : nMaxVal = -1;
2503 :
2504 : static int i;
2505 0 : for( i = 0; i <= nLen2; i++ )
2506 : {
2507 0 : if( pL1[i] + ( pL2[nLen2] - pL2[i] ) > nMaxVal )
2508 : {
2509 0 : nMaxPos = i;
2510 0 : nMaxVal = pL1[i]+( pL2[nLen2] - pL2[i] );
2511 : }
2512 : }
2513 :
2514 : int nRet = HirschbergLCS( pLcs1, pLcs2, nStt1, nStt1 + nMid,
2515 0 : nStt2, nStt2 + nMaxPos );
2516 : nRet += HirschbergLCS( pLcs1 + nRet, pLcs2 + nRet, nStt1 + nMid, nEnd1,
2517 0 : nStt2 + nMaxPos, nEnd2 );
2518 :
2519 0 : return nRet;
2520 : }
2521 :
2522 0 : int LgstCommonSubseq::Find( int *pSubseq1, int *pSubseq2 )
2523 : {
2524 0 : int nStt = 0;
2525 0 : int nCutEnd = 0;
2526 0 : int nEnd1 = rCmp.GetLen1();
2527 0 : int nEnd2 = rCmp.GetLen2();
2528 :
2529 : // Check for corresponding lines in the beginning of the sequences
2530 0 : while( nStt < nEnd1 && nStt < nEnd2 && rCmp.Compare( nStt, nStt ) )
2531 : {
2532 0 : pSubseq1[ nStt ] = nStt;
2533 0 : pSubseq2[ nStt ] = nStt;
2534 0 : nStt++;
2535 : }
2536 :
2537 0 : pSubseq1 += nStt;
2538 0 : pSubseq2 += nStt;
2539 :
2540 : // Check for corresponding lines in the end of the sequences
2541 0 : while( nStt < nEnd1 && nStt < nEnd2
2542 0 : && rCmp.Compare( nEnd1 - 1, nEnd2 - 1 ) )
2543 : {
2544 0 : nCutEnd++;
2545 0 : nEnd1--;
2546 0 : nEnd2--;
2547 : }
2548 :
2549 0 : int nLen = HirschbergLCS( pSubseq1, pSubseq2, nStt, nEnd1, nStt, nEnd2 );
2550 :
2551 0 : for( int i = 0; i < nCutEnd; i++ )
2552 : {
2553 0 : pSubseq1[ nLen + i ] = nEnd1 + i;
2554 0 : pSubseq2[ nLen + i ] = nEnd2 + i;
2555 : }
2556 :
2557 0 : return nStt + nLen + nCutEnd;
2558 : }
2559 :
2560 0 : int FastCommonSubseq::FindFastCS( int *pSeq1, int *pSeq2, int nStt1,
2561 : int nEnd1, int nStt2, int nEnd2 )
2562 : {
2563 0 : int nCutBeg = 0;
2564 0 : int nCutEnd = 0;
2565 :
2566 : // Check for corresponding lines in the beginning of the sequences
2567 0 : while( nStt1 < nEnd1 && nStt2 < nEnd2 && rCmp.Compare( nStt1, nStt2 ) )
2568 : {
2569 0 : pSeq1[ nCutBeg ] = nStt1++;
2570 0 : pSeq2[ nCutBeg ] = nStt2++;
2571 0 : nCutBeg++;
2572 : }
2573 :
2574 0 : pSeq1 += nCutBeg;
2575 0 : pSeq2 += nCutBeg;
2576 :
2577 : // Check for corresponding lines in the end of the sequences
2578 0 : while( nStt1 < nEnd1 && nStt2 < nEnd2
2579 0 : && rCmp.Compare( nEnd1 - 1, nEnd2 - 1 ) )
2580 : {
2581 0 : nCutEnd++;
2582 0 : nEnd1--;
2583 0 : nEnd2--;
2584 : }
2585 :
2586 0 : int nLen1 = nEnd1 - nStt1;
2587 0 : int nLen2 = nEnd2 - nStt2;
2588 :
2589 : // Return if a sequence is empty
2590 0 : if( nLen1 <= 0 || nLen2 <= 0 )
2591 : {
2592 0 : for( int i = 0; i < nCutEnd; i++ )
2593 : {
2594 0 : pSeq1[ i ] = nEnd1 + i;
2595 0 : pSeq2[ i ] = nEnd2 + i;
2596 : }
2597 0 : return nCutBeg + nCutEnd;
2598 : }
2599 :
2600 : // Cut to LCS for small values
2601 0 : if( nLen1 < 3 || nLen2 < 3 || ( nLen1 + 1 ) * ( nLen2 + 1 ) <= CUTOFF )
2602 : {
2603 0 : int nLcsLen = FindLCS( pSeq1, pSeq2, nStt1, nEnd1, nStt2, nEnd2);
2604 :
2605 0 : for( int i = 0; i < nCutEnd; i++ )
2606 : {
2607 0 : pSeq1[ nLcsLen + i ] = nEnd1 + i;
2608 0 : pSeq2[ nLcsLen + i ] = nEnd2 + i;
2609 : }
2610 0 : return nCutBeg + nLcsLen + nCutEnd;
2611 : }
2612 :
2613 0 : int nMid1 = nLen1/2;
2614 0 : int nMid2 = nLen2/2;
2615 :
2616 : int nRad;
2617 0 : int nPos1 = -1, nPos2 = -1;
2618 :
2619 : // Find a point of correspondence in the middle of the sequences
2620 0 : for( nRad = 0; nRad*nRad < std::min( nMid1, nMid2 ); nRad++ )
2621 : {
2622 : // Search to the left and to the right of the middle of the first sequence
2623 0 : for( int i = nMid1 - nRad; i <= nMid1 + nRad; i++ )
2624 : {
2625 0 : if( rCmp.Compare( nStt1 + i, nStt2 + nMid2 - nRad ) )
2626 : {
2627 0 : nPos1 = nStt1 + i;
2628 0 : nPos2 = nStt2 + nMid2 - nRad;
2629 0 : break;
2630 : }
2631 0 : if( rCmp.Compare( nStt1 + i, nStt2 + nMid2 + nRad ) )
2632 : {
2633 0 : nPos1 = nStt1 + i;
2634 0 : nPos2 = nStt2 + nMid2 - nRad;
2635 0 : break;
2636 : }
2637 : }
2638 : // Search to the left and to the right of the middle of the second sequence
2639 0 : for( int i = nMid2 - nRad; i <= nMid2 + nRad; i++ )
2640 : {
2641 0 : if( rCmp.Compare( nStt2 + nMid2 - nRad, nStt2 + i ) )
2642 : {
2643 0 : nPos2 = nStt2 + i;
2644 0 : nPos1 = nStt1 + nMid1 - nRad;
2645 0 : break;
2646 : }
2647 0 : if( rCmp.Compare( nStt2 + nMid2 - nRad, nStt2 + i ) )
2648 : {
2649 0 : nPos2 = nStt2 + i;
2650 0 : nPos1 = nStt1 + nMid1 - nRad;
2651 0 : break;
2652 : }
2653 : }
2654 : }
2655 :
2656 : // return if no point of correspondence found
2657 0 : if( nPos1 == -1 )
2658 : {
2659 0 : for( int i = 0; i < nCutEnd; i++ )
2660 : {
2661 0 : pSeq1[ i ] = nEnd1 + i;
2662 0 : pSeq2[ i ] = nEnd2 + i;
2663 : }
2664 0 : return nCutBeg + nCutEnd;
2665 : }
2666 :
2667 : // Run the same on the sequences to the left of the correspondence point
2668 0 : int nLen = FindFastCS( pSeq1, pSeq2, nStt1, nPos1, nStt2, nPos2 );
2669 :
2670 0 : pSeq1[ nLen ] = nPos1;
2671 0 : pSeq2[ nLen ] = nPos2;
2672 :
2673 : // Run the same on the sequences to the right of the correspondence point
2674 0 : nLen += FindFastCS( pSeq1 + nLen + 1, pSeq2 + nLen + 1,
2675 0 : nPos1 + 1, nEnd1, nPos2 + 1, nEnd2 ) + 1;
2676 :
2677 0 : for( int i = 0; i < nCutEnd; i++ )
2678 : {
2679 0 : pSeq1[ nLen + i ] = nEnd1 + i;
2680 0 : pSeq2[ nLen + i ] = nEnd2 + i;
2681 : }
2682 :
2683 0 : return nLen + nCutBeg + nCutEnd;
2684 177 : }
2685 :
2686 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|