xref: /trunk/main/sw/source/core/txtnode/ndhints.cxx (revision 69a74367)
1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_sw.hxx"
26 
27 
28 
29 #include "txatbase.hxx"
30 #include "ndhints.hxx"
31 #include <txtatr.hxx>
32 
33 #ifndef PRODUCT
34 #include <pam.hxx>
35 #endif
36 
37 
38 _SV_IMPL_SORTAR_ALG( SwpHtStart, SwTxtAttr* )
39 _SV_IMPL_SORTAR_ALG( SwpHtEnd, SwTxtAttr* )
40 
41 #ifdef NIE
42 
43 void DumpHints( const SwpHtStart &rHtStart,
44 				const SwpHtEnd &rHtEnd )
45 {
46 #ifdef DBG_UTIL
47 	aDbstream << "DumpHints:" << endl;
48 	(aDbstream << "\tStarts:" ).WriteNumber(rHtStart.Count()) << endl;
49 	for( sal_uInt16 i = 0; i < rHtStart.Count(); ++i )
50 	{
51 		const SwTxtAttr *pHt = rHtStart[i];
52 		((((aDbstream << '\t').WriteNumber( i )<< " [").WriteNumber( pHt->Which() )
53 			<< ']' << '\t').WriteNumber( long( pHt ) )
54 				  << '\t').WriteNumber( *pHt->GetStart() );
55 		if( pHt->End() )
56 			(aDbstream << " -> " ).WriteNumber( *pHt->End() );
57 		aDbstream << endl;
58 	}
59 	(aDbstream << "\tEnds:").WriteNumber( rHtEnd.Count() )<< endl;
60 	for( i = 0; i < rHtEnd.Count(); ++i )
61 	{
62 		const SwTxtAttr *pHt = rHtEnd[i];
63 		(((aDbstream << '\t').WriteNumber( i )<< " [").WriteNumber( pHt->Which() )
64 			<< ']' << '\t' ).WriteNumber( long( pHt ) );
65 		if( pHt->End() )
66 			(aDbstream << '\t').WriteNumber( *pHt->End() )<< " <- ";
67 		aDbstream.WriteNumber( *pHt->GetStart() )<< endl;
68 	}
69 	aDbstream << endl;
70 #endif
71 }
72 #else
73 inline void DumpHints(const SwpHtStart &, const SwpHtEnd &) { }
74 #endif
75 
76 /*************************************************************************
77  *                        inline IsEqual()
78  *************************************************************************/
79 
80 inline sal_Bool IsEqual( const SwTxtAttr &rHt1, const SwTxtAttr &rHt2 )
81 {
82 	return (long)(&rHt1) == (long)(&rHt2);
83 }
84 
85 /*************************************************************************
86  *                      IsLessStart()
87  *************************************************************************/
88 
89 // SV_IMPL_OP_PTRARR_SORT( SwpHtStart, SwTxtAttr* )
90 // kein SV_IMPL_PTRARR_SORT( name,ArrElement )
91 // unser SEEK_PTR_TO_OBJECT_NOTL( name,ArrElement )
92 
93 // Sortierreihenfolge: Start, Ende (umgekehrt!), Which-Wert (umgekehrt!),
94 // 					   als letztes die Adresse selbst
95 
96 static sal_Bool lcl_IsLessStart( const SwTxtAttr &rHt1, const SwTxtAttr &rHt2 )
97 {
98 	if ( *rHt1.GetStart() == *rHt2.GetStart() )
99 	{
100         const xub_StrLen nHt1 = *rHt1.GetAnyEnd();
101         const xub_StrLen nHt2 = *rHt2.GetAnyEnd();
102 		if ( nHt1 == nHt2 )
103 		{
104             const sal_uInt16 nWhich1 = rHt1.Which();
105             const sal_uInt16 nWhich2 = rHt2.Which();
106             if ( nWhich1 == nWhich2 )
107             {
108                 if ( RES_TXTATR_CHARFMT == nWhich1 )
109                 {
110                     const sal_uInt16 nS1 = static_cast<const SwTxtCharFmt&>(rHt1).GetSortNumber();
111                     const sal_uInt16 nS2 = static_cast<const SwTxtCharFmt&>(rHt2).GetSortNumber();
112                     ASSERT( nS1 != nS2, "AUTOSTYLES: lcl_IsLessStart trouble" )
113                     if ( nS1 != nS2 ) // robust
114                         return nS1 < nS2;
115                 }
116 
117                 return (long)&rHt1 < (long)&rHt2;
118             }
119             // order is important! for requirements see hintids.hxx
120             return ( nWhich1 > nWhich2 );
121         }
122         return ( nHt1 > nHt2 );
123 	}
124 	return ( *rHt1.GetStart() < *rHt2.GetStart() );
125 }
126 
127 /*************************************************************************
128  *                      inline IsLessEnd()
129  *************************************************************************/
130 
131 // Zuerst nach Ende danach nach Ptr
132 static sal_Bool lcl_IsLessEnd( const SwTxtAttr &rHt1, const SwTxtAttr &rHt2 )
133 {
134     const xub_StrLen nHt1 = *rHt1.GetAnyEnd();
135     const xub_StrLen nHt2 = *rHt2.GetAnyEnd();
136 	if ( nHt1 == nHt2 )
137 	{
138 		if ( *rHt1.GetStart() == *rHt2.GetStart() )
139 		{
140             const sal_uInt16 nWhich1 = rHt1.Which();
141             const sal_uInt16 nWhich2 = rHt2.Which();
142             if ( nWhich1 == nWhich2 )
143             {
144                 if ( RES_TXTATR_CHARFMT == nWhich1 )
145                 {
146                     const sal_uInt16 nS1 = static_cast<const SwTxtCharFmt&>(rHt1).GetSortNumber();
147                     const sal_uInt16 nS2 = static_cast<const SwTxtCharFmt&>(rHt2).GetSortNumber();
148                     ASSERT( nS1 != nS2, "AUTOSTYLES: lcl_IsLessEnd trouble" )
149                     if ( nS1 != nS2 ) // robust
150                         return nS1 > nS2;
151                 }
152 
153                 return (long)&rHt1 > (long)&rHt2;
154             }
155             // order is important! for requirements see hintids.hxx
156             return ( nWhich1 < nWhich2 );
157         }
158 		else
159 			return ( *rHt1.GetStart() > *rHt2.GetStart() );
160 	}
161 	return ( nHt1 < nHt2 );
162 }
163 
164 /*************************************************************************
165  *                      SwpHtStart::Seek_Entry()
166  *************************************************************************/
167 
168 sal_Bool SwpHtStart::Seek_Entry( const SwTxtAttr *pElement, sal_uInt16 *pPos ) const
169 {
170 	sal_uInt16 nOben = Count(), nMitte, nUnten = 0;
171 	if( nOben > 0 )
172 	{
173 		nOben--;
174 		while( nUnten <= nOben )
175 		{
176 			nMitte = nUnten + ( nOben - nUnten ) / 2;
177 			const SwTxtAttr *pMitte = (*this)[nMitte];
178 			if( IsEqual( *pMitte, *pElement ) )
179 			{
180 				*pPos = nMitte;
181 				return sal_True;
182 			}
183 			else
184 				if( lcl_IsLessStart( *pMitte, *pElement ) )
185 					nUnten = nMitte + 1;
186 				else
187 					if( nMitte == 0 )
188 					{
189 						*pPos = nUnten;
190 						return sal_False;
191 					}
192 					else
193 						nOben = nMitte - 1;
194 		}
195 	}
196 	*pPos = nUnten;
197 	return sal_False;
198 }
199 
200 /*************************************************************************
201  *                      SwpHtEnd::Seek_Entry()
202  *************************************************************************/
203 
204 sal_Bool SwpHtEnd::Seek_Entry( const SwTxtAttr *pElement, sal_uInt16 *pPos ) const
205 {
206 	sal_uInt16 nOben = Count(), nMitte, nUnten = 0;
207 	if( nOben > 0 )
208 	{
209 		nOben--;
210 		while( nUnten <= nOben )
211 		{
212 			nMitte = nUnten + ( nOben - nUnten ) / 2;
213 			const SwTxtAttr *pMitte = (*this)[nMitte];
214 			if( IsEqual( *pMitte, *pElement ) )
215 			{
216 				*pPos = nMitte;
217 				return sal_True;
218 			}
219 			else
220 				if( lcl_IsLessEnd( *pMitte, *pElement ) )
221 					nUnten = nMitte + 1;
222 				else
223 					if( nMitte == 0 )
224 					{
225 						*pPos = nUnten;
226 						return sal_False;
227 					}
228 					else
229 						nOben = nMitte - 1;
230 		}
231 	}
232 	*pPos = nUnten;
233 	return sal_False;
234 }
235 
236 /*************************************************************************
237  *                      class SwpHintsArr
238  *************************************************************************/
239 
240 void SwpHintsArray::Insert( const SwTxtAttr *pHt )
241 {
242     Resort();
243 #ifdef DBG_UTIL
244     sal_uInt16 nPos;
245     ASSERT(!m_HintStarts.Seek_Entry( pHt, &nPos ),
246             "Insert: hint already in HtStart");
247     ASSERT(!m_HintEnds.Seek_Entry( pHt, &nPos ),
248             "Insert: hint already in HtEnd");
249 #endif
250     m_HintStarts.Insert( pHt );
251     m_HintEnds.Insert( pHt );
252 #ifdef DBG_UTIL
253 #ifdef NIE
254     (aDbstream << "Insert: " ).WriteNumber( long( pHt ) ) << endl;
255     DumpHints( m_HintStarts, m_HintEnds );
256 #endif
257 #endif
258 }
259 
260 void SwpHintsArray::DeleteAtPos( const sal_uInt16 nPos )
261 {
262     // optimization: nPos is the position in the Starts array
263     const SwTxtAttr *pHt = m_HintStarts[ nPos ];
264     m_HintStarts.Remove( nPos );
265 
266     Resort();
267 
268     sal_uInt16 nEndPos;
269     m_HintEnds.Seek_Entry( pHt, &nEndPos );
270     m_HintEnds.Remove( nEndPos );
271 #ifdef DBG_UTIL
272 #ifdef NIE
273     (aDbstream << "DeleteAtPos: " ).WriteNumber( long( pHt ) ) << endl;
274     DumpHints( m_HintStarts, m_HintEnds );
275 #endif
276 #endif
277 }
278 
279 #ifdef DBG_UTIL
280 
281 /*************************************************************************
282  *                      SwpHintsArray::Check()
283  *************************************************************************/
284 
285 
286 #define CHECK_ERR(cond, text) \
287         if(!(cond)) \
288         { \
289             ASSERT(!this, text); \
290             DumpHints(m_HintStarts, m_HintEnds); \
291             return !(const_cast<SwpHintsArray*>(this))->Resort(); \
292         }
293 
294 bool SwpHintsArray::Check() const
295 {
296 	// 1) gleiche Anzahl in beiden Arrays
297     CHECK_ERR( m_HintStarts.Count() == m_HintEnds.Count(),
298         "HintsCheck: wrong sizes" );
299 	xub_StrLen nLastStart = 0;
300 	xub_StrLen nLastEnd   = 0;
301 
302 	const SwTxtAttr *pLastStart = 0;
303 	const SwTxtAttr *pLastEnd = 0;
304 
305 	for( sal_uInt16 i = 0; i < Count(); ++i )
306 	{
307 		// --- Start-Kontrolle ---
308 
309 		// 2a) gueltiger Pointer? vgl. DELETEFF
310         const SwTxtAttr *pHt = m_HintStarts[i];
311 		CHECK_ERR( 0xFF != *(unsigned char*)pHt, "HintsCheck: start ptr was deleted" );
312 
313 		// 3a) Stimmt die Start-Sortierung?
314 		xub_StrLen nIdx = *pHt->GetStart();
315 		CHECK_ERR( nIdx >= nLastStart, "HintsCheck: starts are unsorted" );
316 
317 		// 4a) IsLessStart-Konsistenz
318 		if( pLastStart )
319 			CHECK_ERR( lcl_IsLessStart( *pLastStart, *pHt ), "HintsCheck: IsLastStart" );
320 
321 		nLastStart = nIdx;
322 		pLastStart = pHt;
323 
324 		// --- End-Kontrolle ---
325 
326 		// 2b) gueltiger Pointer? vgl. DELETEFF
327         const SwTxtAttr *pHtEnd = m_HintEnds[i];
328 		CHECK_ERR( 0xFF != *(unsigned char*)pHtEnd, "HintsCheck: end ptr was deleted" );
329 
330 		// 3b) Stimmt die End-Sortierung?
331 		nIdx = *pHtEnd->GetAnyEnd();
332 		CHECK_ERR( nIdx >= nLastEnd, "HintsCheck: ends are unsorted" );
333 		nLastEnd = nIdx;
334 
335 		// 4b) IsLessEnd-Konsistenz
336 		if( pLastEnd )
337 			CHECK_ERR( lcl_IsLessEnd( *pLastEnd, *pHtEnd ), "HintsCheck: IsLastEnd" );
338 
339 		nLastEnd = nIdx;
340 		pLastEnd = pHtEnd;
341 
342 		// --- Ueberkreuzungen ---
343 
344 		// 5) gleiche Pointer in beiden Arrays
345         if( !m_HintStarts.Seek_Entry( pHt, &nIdx ) )
346             nIdx = STRING_LEN;
347 
348         CHECK_ERR( STRING_LEN != nIdx, "HintsCheck: no GetStartOf" );
349 
350 		// 6) gleiche Pointer in beiden Arrays
351         if( !m_HintEnds.Seek_Entry( pHt, &nIdx ) )
352             nIdx = STRING_LEN;
353 
354         CHECK_ERR( STRING_LEN != nIdx, "HintsCheck: no GetEndOf" );
355 
356         // 7a) character attributes in array?
357         sal_uInt16 nWhich = pHt->Which();
358         CHECK_ERR( !isCHRATR(nWhich),
359                    "HintsCheck: Character attribute in start array" );
360 
361         // 7b) character attributes in array?
362         nWhich = pHtEnd->Which();
363         CHECK_ERR( !isCHRATR(nWhich),
364                    "HintsCheck: Character attribute in end array" );
365 
366         // 8) style portion check
367 #if OSL_DEBUG_LEVEL > 1
368         const SwTxtAttr* pHtThis = m_HintStarts[i];
369         const SwTxtAttr* pHtLast = i > 0 ? m_HintStarts[i-1] : 0;
370         CHECK_ERR( 0 == i ||
371                     ( RES_TXTATR_CHARFMT != pHtLast->Which() && RES_TXTATR_AUTOFMT != pHtLast->Which() ) ||
372                     ( RES_TXTATR_CHARFMT != pHtThis->Which() && RES_TXTATR_AUTOFMT != pHtThis->Which() ) ||
373                     ( *pHtThis->GetStart() >= *pHtLast->End() ) ||
374                     (   (   (   (*pHtThis->GetStart() == *pHtLast->GetStart())
375                             &&  (*pHtThis->End()   == *pHtLast->End())
376                             ) // same range
377                         ||  (*pHtThis->GetStart() == *pHtThis->End())
378                         )
379                     &&  (   (pHtThis->Which() != RES_TXTATR_AUTOFMT)
380                         ||  (pHtLast->Which() != RES_TXTATR_AUTOFMT)
381                         ) // never two AUTOFMT on same range
382                     ),
383                    "HintsCheck: Portion inconsistency. "
384                    "This can be temporarily ok during undo operations" );
385 
386         // 9) nesting portion check
387         if (pHtThis->IsNesting())
388         {
389             for ( sal_uInt16 j = 0; j < Count(); ++j )
390             {
391                 SwTxtAttr const * const pOther( m_HintStarts[j] );
392                 if ( pOther->IsNesting() &&  (i != j) )
393                 {
394                     SwComparePosition cmp = ComparePosition(
395                         *pHtThis->GetStart(), *pHtThis->End(),
396                         *pOther->GetStart(), *pOther->End());
397                     CHECK_ERR( (POS_OVERLAP_BEFORE != cmp) &&
398                                (POS_OVERLAP_BEHIND != cmp),
399                         "HintsCheck: overlapping nesting hints!!!" );
400                 }
401             }
402         }
403 
404         // 10) dummy char check (unfortunately cannot check SwTxtNode::m_Text)
405         if (pHtThis->HasDummyChar())
406         {
407             for ( sal_uInt16 j = 0; j < i; ++j )
408             {
409                 SwTxtAttr const * const pOther( m_HintStarts[j] );
410                 if (pOther->HasDummyChar())
411                 {
412                     CHECK_ERR( (*pOther->GetStart() != *pHtThis->GetStart()),
413                         "HintsCheck: multiple hints claim same CH_TXTATR!");
414                 }
415             }
416         }
417 #endif
418     }
419     return true;
420 }
421 
422 #endif      /* PRODUCT */
423 
424 /*************************************************************************
425  *                          SwpHintsArray::Resort()
426  *************************************************************************/
427 
428 // Resort() wird vor jedem Insert und Delete gerufen.
429 // Wenn Textmasse geloescht wird, so werden die Indizes in
430 // ndtxt.cxx angepasst. Leider erfolgt noch keine Neusortierung
431 // auf gleichen Positionen.
432 
433 bool SwpHintsArray::Resort()
434 {
435     bool bResort = false;
436 	const SwTxtAttr *pLast = 0;
437 	sal_uInt16 i;
438 
439     for ( i = 0; i < m_HintStarts.Count(); ++i )
440     {
441         const SwTxtAttr *pHt = m_HintStarts[i];
442 		if( pLast && !lcl_IsLessStart( *pLast, *pHt ) )
443 		{
444 #ifdef NIE
445 #ifdef DBG_UTIL
446 //            ASSERT( bResort, "!Resort/Start: correcting hints-array" );
447 			aDbstream << "Resort: Starts" << endl;
448             DumpHints( m_HintStarts, m_HintEnds );
449 #endif
450 #endif
451             m_HintStarts.Remove( i );
452             m_HintStarts.Insert( pHt );
453             pHt = m_HintStarts[i];
454 			if ( pHt != pLast )
455 				--i;
456             bResort = true;
457         }
458 		pLast = pHt;
459 	}
460 
461 	pLast = 0;
462     for ( i = 0; i < m_HintEnds.Count(); ++i )
463     {
464         const SwTxtAttr *pHt = m_HintEnds[i];
465 		if( pLast && !lcl_IsLessEnd( *pLast, *pHt ) )
466 		{
467 #ifdef NIE
468 #ifdef DBG_UTIL
469 			aDbstream << "Resort: Ends" << endl;
470             DumpHints( m_HintStarts, m_HintEnds );
471 #endif
472 #endif
473             m_HintEnds.Remove( i );
474             m_HintEnds.Insert( pHt );
475             pHt = m_HintEnds[i]; // normalerweise == pLast
476 			// Wenn die Unordnung etwas groesser ist (24200),
477 			// muessen wir Position i erneut vergleichen.
478 			if ( pLast != pHt )
479 				--i;
480             bResort = true;
481         }
482 		pLast = pHt;
483 	}
484 #ifdef DBG_UTIL
485 #ifdef NIE
486 	aDbstream << "Resorted:" << endl;
487     DumpHints( m_HintStarts, m_HintEnds );
488 #endif
489 #endif
490 	return bResort;
491 }
492 
493 
494