1 /* AbiWord
2  * Copyright (C) 1998 AbiSource, Inc.
3  * Copyright (c) 2001,2002 Tomas Frydrych
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18  * 02110-1301 USA.
19  */
20 
21 
22 #include <stdlib.h>
23 #include <string.h>
24 #include <ctype.h>
25 
26 #include "ut_types.h"
27 #include "ut_assert.h"
28 #include "ut_debugmsg.h"
29 #include "ut_string.h"
30 #include "ut_string_class.h"
31 #include "ut_vector.h"
32 
33 #include "pt_Types.h"
34 
35 #include "pp_AttrProp.h"
36 #include "pp_Revision.h"
37 #include "pd_Style.h"
38 #include "pd_Document.h"
39 
40 
41 /****************************************************************/
42 
43 /// This is the sole explicit constructor of class PP_AttrProp.
PP_AttrProp()44 PP_AttrProp::PP_AttrProp()
45 {
46 	m_pAttributes = NULL;
47 	m_pProperties = NULL;
48 	m_szProperties = NULL;
49 	m_bIsReadOnly = false;
50 	m_checkSum = 0;
51 	xxx_UT_DEBUGMSG(("creating pp_AttrProp %x \n",this));
52 
53 	m_iRevisedIndex = 0xffffffff;
54 	m_bRevisionHidden = false;
55 }
56 
57 /// This is the sole explicit destructor of class PP_AttrProp.
~PP_AttrProp()58 PP_AttrProp::~PP_AttrProp()
59 {
60 	xxx_UT_DEBUGMSG(("deleting pp_AttrProp %x \n",this));
61 	if (m_pAttributes)
62 	{
63 		UT_GenericStringMap<gchar*>::UT_Cursor c1(m_pAttributes);
64 
65 		const gchar * s = c1.first();
66 
67 		while (true) {
68 			FREEP(s);
69 
70 			if (!c1.is_valid())
71 				break;
72 
73 			s = c1.next();
74 		}
75 
76 		delete m_pAttributes;
77 		m_pAttributes = NULL;
78 	}
79 
80 	// delete any PP_Property_types;
81 
82 	if(m_pProperties)
83 	{
84 		UT_GenericStringMap<PropertyPair*>::UT_Cursor c(m_pProperties);
85 		const PropertyPair * entry = NULL;
86 
87 		for (entry = c.first(); c.is_valid(); entry = c.next())
88 		{
89 			if(entry)
90 			{
91 				// hack. don't do it.
92 				gchar* tmp = (gchar*)entry->first;
93 				FREEP(tmp);
94 				if (entry->second)
95 				{
96 					delete entry->second;
97 				}
98 				delete entry;
99 			}
100 		}
101 
102 		delete m_pProperties;
103 		m_pProperties = NULL;
104 	}
105 	if(m_szProperties)
106 	{
107 		delete [] m_szProperties;
108 	}
109 	m_szProperties = NULL;
110 }
111 
112 /*!
113  * Returns the number of properties in this PP_AttrProp.
114  *
115  * BE WEARY, BE VERY WEARY, the count can be greater than the number of valid items
116  * stored in the hash -- always check the return value of getNthProperty()
117  */
getPropertyCount(void) const118 size_t PP_AttrProp::getPropertyCount (void) const
119 {
120 	if(!m_pProperties)
121 		return 0;
122 	else
123 		return m_pProperties->size();
124 }
125 
126 /*!
127  * Returns the number of attributes in this PP_AttrProp.
128  *
129  * BE WEARY, BE VERY WEARY, the count can be greater than the number of valid items
130  * stored in the hash -- always check the return value of getNthAttribute()
131  */
getAttributeCount(void) const132 size_t PP_AttrProp::getAttributeCount (void) const
133 {
134 	if(!m_pAttributes)
135 		return 0;
136 	else
137 		return m_pAttributes->size();
138 }
139 
140 
141 /*!
142  * Sets attributes as given in the NULL-terminated input array
143  * of (attribute, value) pairs.
144  *
145  * \param attributes An array of strings, read in (attribute, value) form.
146  */
setAttributes(const gchar ** attributes)147 bool	PP_AttrProp::setAttributes(const gchar ** attributes)
148 {
149 	if (!attributes)
150 		return true;
151 
152 	const gchar ** pp = attributes;
153 	while (*pp)
154 	{
155 		if (!setAttribute(pp[0],pp[1]))
156 			return false;
157 		pp += 2;
158 	}
159 	return true;
160 }
161 
162 /*!
163  * Sets attributes as given in the UT_Vector of strings, read as
164  * (attribute, value) pairs.
165  *
166  * \param attributes A UT_Vector of strings, read in (attribute, value) form.
167  */
setAttributes(const UT_GenericVector<const gchar * > * pVector)168 bool PP_AttrProp::setAttributes(const UT_GenericVector<const gchar*> * pVector)
169 {
170 	UT_uint32 kLimit = pVector->getItemCount();
171 	for (UT_uint32 k=0; k+1<kLimit; k+=2)
172 	{
173 		const gchar * pName = pVector->getNthItem(k);
174 		const gchar * pValue = pVector->getNthItem(k+1);
175 		if (!setAttribute(pName,pValue))
176 			return false;
177 	}
178 	return true;
179 }
180 
181 /*!
182  * Sets attributes as given in the NULL-terminated input array
183  * of (attribute, value) pairs.
184  */
setProperties(const gchar ** properties)185 bool	PP_AttrProp::setProperties(const gchar ** properties)
186 {
187 	if (!properties)
188 		return true;
189 
190 	const gchar ** pp = properties;
191 	while (*pp)
192 	{
193 		if (!setProperty(pp[0],pp[1]))
194 			return false;
195 		pp += 2;
196 	}
197 	return true;
198 }
199 
200 /*!
201  * Sets properties as given in the UT_Vector of strings, read as
202  * (property, value) pairs.
203  *
204  * \param pVector A UT_Vector of strings, read in (properties, value) form.
205  */
setProperties(const UT_GenericVector<gchar * > * pVector)206 bool PP_AttrProp::setProperties(const UT_GenericVector<gchar*> * pVector)
207 {
208 	UT_uint32 kLimit = pVector->getItemCount();
209 	for (UT_uint32 k=0; k+1<kLimit; k+=2)
210 	{
211 		const gchar * pName = pVector->getNthItem(k);
212 		const gchar * pValue = pVector->getNthItem(k+1);
213 		if (!setProperty(pName,pValue))
214 			return false;
215 	}
216 	return true;
217 }
218 
219 
220 /*!
221  * Sets given attribute in this PP_AttrProp bundle.
222  * Deals correctly with setting the PT_PROPS_ATTRIBUTE_NAME property:
223  * intercepts this call and appends properties instead.
224  *
225  * Because all mutations of attributes go through here, it is always the
226  * case that the props attribute is correctly handled.
227  */
setAttribute(const gchar * szName,const gchar * szValue)228 bool	PP_AttrProp::setAttribute(const gchar * szName, const gchar * szValue)
229 {
230 	// TODO when this assert fails, switch this file to use UT_XML_ version of str*() functions.
231 	UT_return_val_if_fail (sizeof(char)==sizeof(gchar), false);
232 
233 	if (0 == strcmp(szName, PT_PROPS_ATTRIBUTE_NAME) && *szValue)	// PROPS -- cut value up into properties
234 	{
235 		char * pOrig = NULL;
236 
237 		if (!(pOrig = g_strdup(szValue)))
238 		{
239 			UT_DEBUGMSG(("setAttribute: g_strdup() failed on [%s]\n",szValue));
240 			return false;
241 		}
242 
243 		// This function parses out CSS properties, separated by semicolons.
244 
245 		char *z = pOrig;
246 		int bDone = 0;
247 		while (!bDone)
248 		{
249 			// p will point to the property name.  q will be the property value.
250 			char *p = z;
251 			char *q = p;
252 			// skip the whitespace before the property name
253 			while (isspace(*p))
254 				p++;
255 
256 			// skip to the colon to find the value
257 			while (*q && (*q != ':'))
258 				q++;
259 
260 			// if there was no colon, this is invalid
261 			if (!*q)
262 			{
263 				g_free(pOrig);
264 				UT_DEBUGMSG(("props: %s\n", szValue));
265 				return false;
266 			}
267 
268 			// zero-out the colon, thus separating into two strings.
269 			*q = 0;
270 			q++;
271 
272 			// now, search ahead for the next semicolon, and separate this property from the next
273 			z = q;
274 			while (*z && (*z != ';'))
275 				z++;
276 
277 			if (*z == ';')
278 			{
279 				*z = 0;
280 				z++;
281 			}
282 			else
283 			{
284 				bDone = 1;
285 			}
286 
287 			// skip the whitespace before the property value
288 			while ((*q > 0) && isspace(*q))
289 				q++;
290 
291 			setProperty(p, q);
292 		}
293 
294 		g_free(pOrig);
295 		return true;
296 	}
297 	else if (0 == strcmp(szName, PT_XID_ATTRIBUTE_NAME) && *szValue)
298 	{
299 		// XID is a unique id for the xml element / PT frag. Its function is to facilitate
300 		// comparing/merging documents and we do not want it in the AP
301 		return true;
302 	}
303 	else // not "PROPS" -- add to attribute list
304 	{
305 		UT_UTF8String url;
306 		if (szValue && *szValue && (0 == strcmp(szName, "xlink:href") || 0 == strcmp(szName, "href")))
307 		{
308 			url = szValue;
309 			url.decodeURL();
310 			szValue = url.utf8_str();
311 		}
312 
313 		if (!m_pAttributes)
314 		{
315 			m_pAttributes = new UT_GenericStringMap<gchar*>(5);
316 			if (!m_pAttributes)
317 			{
318 				UT_DEBUGMSG(("setAttribute: could not allocate hash table.\n"));
319 				return false;
320 			}
321 		}
322 
323 		// make sure we store attribute names in lowercase
324 		UT_ASSERT_HARMLESS(sizeof(char) == sizeof(gchar));
325 
326 		char * copy = g_ascii_strdown(szName, -1);
327 		char * szDupValue = szValue ? g_strdup(szValue) : NULL;
328 
329 		// get rid of any illegal chars we might have been given
330 		if(!UT_isValidXML(copy))
331 			UT_validXML(copy);
332 		if(!UT_isValidXML(szDupValue))
333 			UT_validXML(szDupValue);
334 
335 		const char * pEntry = m_pAttributes->pick(copy);
336 
337 		if(pEntry)
338 		{
339 			// attribute exists, replace it
340 			FREEP(pEntry);
341 			m_pAttributes->set(copy, szDupValue);
342 		}
343 		else
344 		{
345 			bool bRet = m_pAttributes->insert(copy, szDupValue);
346 			UT_ASSERT_HARMLESS( bRet );
347 			if(!bRet)
348 			{
349 				FREEP(szDupValue);
350 			}
351 		}
352 
353 		FREEP(copy);
354 
355 		return true;
356 	}
357 }
358 
359 /*! This method inserts a new pair of property name and value into [this] APs set of
360 	 properties, creating "props" if it does not already exist and overwriting the value
361 	 of any property of the same name with the newly passed value.
362 	 (?)It appears as though we replace the entire pair, rather than only the value.
363 		 (?)Is there a reason for this?
364 	 \return Whether or not the operation succeeded.
365 */
setProperty(const gchar * szName,const gchar * szValue)366 bool	PP_AttrProp::setProperty(const gchar * szName, const gchar * szValue)
367 {
368 	UT_return_val_if_fail( szName, false );
369 
370 	if (!m_pProperties)
371 	{
372 		m_pProperties = new UT_GenericStringMap<PropertyPair*>(5);
373 		if (!m_pProperties)
374 		{
375 			UT_DEBUGMSG(("setProperty: could not allocate hash table.\n"));
376 			return false;
377 		}
378 	}
379 
380 	// if szValue == NULL or *szValue == 0, indicates absent property.
381 	// We have to set it empty, otherwise the code that changes
382 	// properties has no way of knowing that this property is not to
383 	// be present
384 	//
385 	//bool bRemove = (!szValue || !*szValue);
386 
387 	// get rid of any chars invalid in xml
388 	char * szName2 = NULL;
389 	if(!UT_isValidXML(szName))
390 	{
391 		szName2 = g_strdup(szName);
392 
393 		// get rid of any illegal chars we were passed
394 		UT_validXML(szName2);
395 
396 		szName = szName2;
397 	}
398 
399 	char * szValue2 = szValue ? g_strdup(szValue) : NULL;
400 	UT_return_val_if_fail( szName && (szValue2 || !szValue), false);
401 
402 	// get rid of any illegal chars we might have been given in the value
403 	if(!UT_isValidXML(szValue2))
404 		UT_validXML(szValue2);
405 
406 	const PropertyPair * pEntry = m_pProperties->pick(szName);
407 	if (pEntry)
408 	{
409 		const PropertyPair* p = pEntry;
410 
411 		// hack. don't do it.
412 		gchar* tmp = (gchar*)p->first;
413 		UT_return_val_if_fail (!m_bIsReadOnly, false);
414 		if(strcmp(szName,"line-height") == 0)
415 		{
416 			UT_DEBUGMSG(("Found line-height, Old value %s new value is %s \n",tmp,szValue));
417 		}
418 
419 		FREEP(tmp);
420 		if (p->second)
421 		{
422 			delete p->second;
423 		}
424 		delete p;
425 		m_pProperties->set(szName,
426 				   new PropertyPair(szValue2,
427 						    (const PP_PropertyType*)NULL));
428 	}
429 	else
430 	{
431 		m_pProperties->insert(szName,
432 				      new PropertyPair(szValue2,
433 						       (const PP_PropertyType*)NULL));
434 	}
435 
436 	// g_free the name duplicate if necessary
437 	FREEP(szName2);
438 
439 	return true;
440 }
441 
442 /*! This method finds the Nth attribute where N is
443 	 \param ndx The number in order of the attribute to be found
444 	 and assigns that attribute's name and value to
445 	 \param szName The name of the attribute found
446 	 \param szValue The value of the attribute found
447 	 respectively.  The method returns
448 	 \return whether or not the operation succeeded.
449 
450     WARNING: Always check the return value before trying to work with szValue!
451 */
getNthAttribute(int ndx,const gchar * & szName,const gchar * & szValue) const452 bool	PP_AttrProp::getNthAttribute(int ndx, const gchar *& szName, const gchar *& szValue) const
453 {
454 	if (!m_pAttributes)
455 		return false;
456 	if (static_cast<UT_uint32>(ndx) >= m_pAttributes->size())
457 	{
458 		// UT_ASSERT_HARMLESS( UT_SHOULD_NOT_HAPPEN ); -- do not assert, some code in
459 		// while loops relies on this
460 		return false;
461 	}
462 
463 	int i = 0;
464 	UT_GenericStringMap<gchar*>::UT_Cursor c(m_pAttributes);
465 	const gchar * val = NULL;
466 
467 	for (val = c.first(); (c.is_valid() && (i < ndx)); val = c.next(), i++)
468 	{
469 	  // noop
470 	}
471 
472 	if ((i == ndx) && c.is_valid())
473 	  {
474 	    szName = c.key().c_str();
475 	    szValue = val;
476 	    return true;
477 	  }
478 	return false;
479 }
480 
481 /*! This method finds the Nth property where N is
482 	 \param ndx The number in order of the property to be found
483 	 and assigns that property's name and value to
484 	 \param szName The name of the property found
485 	 \param szValue The value of the property found
486 	 respectively.  The method returns
487 	 \return whether or not the operation succeeded.
488 
489     WARNING: Always check the return value before trying to work with szValue!
490 */
getNthProperty(int ndx,const gchar * & szName,const gchar * & szValue) const491 bool	PP_AttrProp::getNthProperty(int ndx, const gchar *& szName, const gchar *& szValue) const
492 {
493 	if (!m_pProperties)
494 		return false;
495 
496  	if (static_cast<UT_uint32>(ndx) >= m_pProperties->size())
497 	{
498 		// UT_ASSERT_HARMLESS( UT_SHOULD_NOT_HAPPEN ); -- do not assert, some code in
499 		// while loops relies on this
500   		return false;
501 	}
502 
503  	int i = 0;
504 	UT_GenericStringMap<PropertyPair*>::UT_Cursor c(m_pProperties);
505  	const PropertyPair * val = NULL;
506 
507 	for (val = c.first(); (c.is_valid() && (i < ndx)); val = c.next(), i++)
508  	{
509 	  // noop
510  	}
511 
512 	if ( (i == ndx) && c.is_valid())
513  		{
514 		  szName = c.key().c_str();
515 		  szValue = val->first;
516 		  return true;
517  		}
518 	return false;
519 }
520 
521 /*! This method finds the property indicated by name
522 	 \param szName (the name of the property the value of which to find)
523 	 and assigns its value to
524 	 \param szValue (the value found of the property requested)
525 	 or returns false if the properties as a whole or the property requested are not found.
526 	 It returns
527 	 \return whether or not the operation succeeded.
528 
529 	 WARNING: Be sure to check the return value before trying to work with szValue.
530 */
getProperty(const gchar * szName,const gchar * & szValue) const531 bool PP_AttrProp::getProperty(const gchar * szName, const gchar *& szValue) const
532 {
533 	if (!m_pProperties)
534 		return false;
535 	const PropertyPair * pEntry = m_pProperties->pick(szName);
536 
537 	if (!pEntry)
538 		return false;
539 
540 	szValue = pEntry->first;
541 	return true;
542 }
543 /*! This method retrieves the entirety of [this] AP's "props", and returns it as an array of
544 	 gchar * pairs (name and value).
545 
546     WARNING: Do not g_free this memory. It's cached here.
547 */
getProperties() const548 const gchar ** PP_AttrProp::getProperties () const
549 {
550 	if(!m_pProperties)
551 		return NULL;
552 	if(m_szProperties != NULL)
553 	{
554 		return m_szProperties;
555 	}
556 	UT_uint32 iPropsCount = m_pProperties->size();
557 	m_szProperties = new const gchar * [iPropsCount*2+2];
558 
559 	const gchar ** pList = m_pProperties->list();
560 	UT_uint32 i = 0;
561 
562 
563 	// where the values should be, we actually have pointers to PropertyPair;
564 	for(i = 1; i < iPropsCount * 2; i += 2)
565 	{
566 		PropertyPair * pP = (PropertyPair *) pList[i];
567 		m_szProperties[i-1] = pList[i-1];
568 		m_szProperties[i] = pP->first;
569 	}
570 	m_szProperties[i-1] = NULL;
571 	m_szProperties[i] = NULL;
572 	return m_szProperties;
573 }
574 
575 /*! (?)TODO: PLEASE DOCUMENT ME!
576 */
getPropertyType(const gchar * szName,tProperty_type Type) const577 const PP_PropertyType *PP_AttrProp::getPropertyType(const gchar * szName, tProperty_type Type) const
578 {
579 	if (!m_pProperties)
580 		return NULL;
581 
582 	const PropertyPair * pEntry = m_pProperties->pick(szName);
583 	if (!pEntry)
584 		return NULL;
585 
586 	if(!pEntry->second)
587 	{
588 		m_pProperties->set(szName, new PropertyPair(pEntry->first,
589 				    PP_PropertyType::createPropertyType(Type,pEntry->first)));
590 		delete pEntry;
591 		pEntry = m_pProperties->pick(szName);
592 	}
593 
594 	return pEntry->second;
595 }
596 
597 /*! This method finds the attribute indicated by name
598 	 \param szName (the name of the attribute the value of which to find)
599 	 and assigns its value to
600 	 \param szValue (the value found of the attribute requested)
601 	 or returns false if the attributes as a whole or the attribute requested are not found.
602 	 It returns
603 	 \return whether or not the operation succeeded.
604 
605 	 WARNING: Be sure to check the return value before trying to work with szValue.
606 */
getAttribute(const gchar * szName,const gchar * & szValue) const607 bool PP_AttrProp::getAttribute(const gchar * szName, const gchar *& szValue) const
608 {
609 	if (!m_pAttributes)
610 		return false;
611 
612 	const gchar * pEntry = m_pAttributes->pick(szName);
613 	if (!pEntry)
614 		return false;
615 
616 	szValue = pEntry;
617 
618 
619 	xxx_UT_DEBUGMSG(("SEVIOR: getAttribute Found value %s \n",szValue));
620 
621 	return true;
622 }
623 
624 /// Returns whether or not the AP has any properties.
625 /*! This method checks [this] AP for the "props" attribute.
626 	 The "props" attribute is a special attribute that contains
627 	 properties.  If the "props" attribute is absent, the
628 	 method returns false.  If the "props" attribute is present
629 	 but empty (m_pProperties->size() == 0), it returns false.
630 	 If the "props" attribute is present and has something in it,
631 	 (m_pProperties->size() > 0), it returns true, but beware that
632 	 no sanity checking is done to make sure that whatever is in
633 	 there is anything more legible than uninitialized memory.
634 */
hasProperties(void) const635 bool PP_AttrProp::hasProperties(void) const
636 {
637 	if (!m_pProperties)
638 		return false;
639 
640 	return (m_pProperties->size() > 0);
641 }
642 
643 /// Returns whether or not the AP has any attributes.
644 /*! This method checks [this] AP for the presence of any attribute.
645 	 If there is any attribute at all in the AP, m_pAttributes->size()
646 	 returns positive and so does this method.  Otherwise, this method
647 	 returns false.
648 	 Beware that no sanity checking is done to make sure that whatever
649 	 is in there (being counted by size()) is anything more legible
650 	 than uninitialized memory.
651 */
hasAttributes(void) const652 bool PP_AttrProp::hasAttributes(void) const
653 {
654 	if (!m_pAttributes)
655 		return false;
656 
657 	return (m_pAttributes->size() > 0);
658 }
659 
660 /// Returns whether or not the given attributes and properties are identically present in [this] AP.
661 /*! This method compares the given attributes and properties with those already present.
662 	 It compares the given items as inseparable pairs - if the attribute or property is
663 	 present in name but contains a different value, that does not count and false is
664 	 returned.
665 	 \return A bool indicating (directly) both presence and equality.
666 */
areAlreadyPresent(const gchar ** attributes,const gchar ** properties) const667 bool PP_AttrProp::areAlreadyPresent(const gchar ** attributes, const gchar ** properties) const
668 {
669 	if (attributes && *attributes)
670 	{
671 		const gchar ** p = attributes;
672 		while (*p)
673 		{
674 			/*
675 			    It seems that we also want empty strings for attributes,
676 				at least for the 'param' attribute which goes with fields.
677 				Is this bogus too? -PL
678 
679 				Yes.
680 				We use empty strings and NULL in values to indicate that the
681 				attribute/property should be absent. Sometimes these values filter down
682 				here, so we need to handle this. TF
683 			*/
684 			// UT_return_val_if_fail (p[1] /* && *p[1]*/, false);	// require value for each name
685 
686 			// first deal with the case where the value is set to NULL or "" -- we want
687 			// that attribute to be absent, not present
688 			const gchar * szValue = NULL;
689 
690 			if((!p[1] || !*p[1]) && getAttribute(p[0],szValue) && szValue && *szValue)
691 				return false;
692 			// the 'props' attribute has to be handled separatedly, since it is not
693 			// returned using getAttribute() (it is not stored as attribute)
694 			else if((!p[1] || !*p[1]) && !strcmp(p[0],"props") && hasProperties())
695 				return false;
696 			else if(p[1] && *p[1])
697 			{
698 				if (!getAttribute(p[0],szValue))
699 					return false;		// item not present
700 				if (strcmp(p[1],szValue)!=0)
701 					return false;		// item has different value
702 			}
703 
704 			p += 2;
705 		}
706 	}
707 
708 	if (properties && *properties)
709 	{
710 		const gchar ** p = properties;
711 		while (*p)
712 		{
713 			/*
714 				Jeff, I weakened the following assert because we
715 				*want* to represent no tabstops as an empty string.
716 				If this isn't safe, let me know.   -- PCR
717 
718 				We use empty strings and NULL in values to indicate that the
719 				attribute/property should be absent. Sometimes these values filter down
720 				here, so we need to handle this. TF
721 			*/
722 			// UT_return_val_if_fail (p[1] /* && *p[1]*/, false);	// require value for each name
723 
724 			// first deal with the case where the value is set to NULL or "" -- we want
725 			// that attribute to be absent, not present
726 			const gchar * szValue = NULL;
727 
728 			if((!p[1] || !*p[1]) && getProperty(p[0],szValue) && szValue && *szValue)
729 				return false;
730 			else if(p[1] && *p[1])
731 			{
732 				if (!getProperty(p[0],szValue))
733 					return false;		// item not present
734 				if (strcmp(p[1],szValue)!=0)
735 					return false;		// item has different value
736 			}
737 
738 			p += 2;
739 		}
740 	}
741 
742 	return true;						// everything matched
743 }
744 
745 /*! Find out if any attribute- or property-name is present.
746 	 This is like areAlreadyPresent(), but we don't care about
747 	 the values, and it returns true after the first discovery
748 	 regardless of whether or not any other of the given names are present.
749 	 \return A bool that's TRUE if any of the given attr. or prop. names is present, false otherwise.
750 */
areAnyOfTheseNamesPresent(const gchar ** attributes,const gchar ** properties) const751 bool PP_AttrProp::areAnyOfTheseNamesPresent(const gchar ** attributes, const gchar ** properties) const
752 {
753 	// TODO consider using the fact that we are now (Dec 12 1998) using
754 	// TODO alpha-hash-table rather than just a hash-table to optimize
755 	// TODO these loops somewhat.
756 
757 	if (attributes && *attributes)
758 	{
759 		const gchar ** p = attributes;
760 		while (*p)
761 		{
762 			const gchar * szValue = NULL;
763 			if (getAttribute(p[0],szValue))
764 				return true;
765 			p += 2;						// skip over value
766 		}
767 	}
768 
769 	if (properties && *properties)
770 	{
771 		const gchar ** p = properties;
772 		while (*p)
773 		{
774 			const gchar * szValue = NULL;
775 			if (getProperty(p[0],szValue))
776 				return true;
777 			p += 2;						// skip over value
778 		}
779 	}
780 
781 	return false;					// didn't find any
782 }
783 
784 /*! Checks to see if the given AP is identical to itself ([this]).  It also contains
785 	 some useful points of instrumentation for benchmarking table and usage characteristics.
786 	 \return TRUE, if and only if we match the AP given, false otherwise.
787 */
isExactMatch(const PP_AttrProp * pMatch) const788 bool PP_AttrProp::isExactMatch(const PP_AttrProp * pMatch) const
789 {
790 	// The counters below are used in testing to profile call and chksum characteristics,
791 	// including collision rates.
792 	// NB: I'm not sure this initialization block is in the correct place.
793 #ifdef PT_TEST
794 	static UT_uint32 s_Calls = 0;
795 	static UT_uint32 s_PassedCheckSum = 0;
796 	static UT_uint32 s_Matches = 0;
797 #endif
798 #ifdef PT_TEST
799 	s_Calls++;
800 #endif
801 
802 	UT_return_val_if_fail (pMatch, false);
803 	//
804 	// Why is this here? Nothing is being changed?
805 	//	UT_return_val_if_fail (m_bIsReadOnly && pMatch->m_bIsReadOnly, false);
806 	if (m_checkSum != pMatch->m_checkSum)
807 		return false;
808 
809 #ifdef PT_TEST
810 	s_PassedCheckSum++;
811 #endif
812 
813 	UT_uint32 countMyAttrs = ((m_pAttributes) ? m_pAttributes->size() : 0);
814 	UT_uint32 countMatchAttrs = ((pMatch->m_pAttributes) ? pMatch->m_pAttributes->size() : 0);
815 	if (countMyAttrs != countMatchAttrs)
816 		return false;
817 
818 	UT_uint32 countMyProps = ((m_pProperties) ? m_pProperties->size() : 0);
819 	UT_uint32 countMatchProps = ((pMatch->m_pProperties) ? pMatch->m_pProperties->size() : 0);
820 	if (countMyProps != countMatchProps)
821 		return false;
822 
823 	if (countMyAttrs != 0)
824 	{
825 		UT_GenericStringMap<gchar*>::UT_Cursor ca1(m_pAttributes);
826 		UT_GenericStringMap<gchar*>::UT_Cursor ca2(pMatch->m_pAttributes);
827 
828 		const gchar * v1 = ca1.first();
829 		const gchar * v2 = ca2.first();
830 
831 		do
832 		{
833 			const gchar *l1 = ca1.key().c_str();
834 			const gchar *l2 = ca2.key().c_str();
835 
836 			if (strcmp(l1, l2) != 0)
837 				return false;
838 
839 			l1 = v1;
840 			l2 = v2;
841 
842 			if (strcmp(l1,l2) != 0)
843 				return false;
844 
845 			v1 = ca1.next();
846 			v2 = ca2.next();
847 		} while (ca1.is_valid() && ca2.is_valid());
848 	}
849 
850 	if (countMyProps > 0)
851 	{
852 		UT_GenericStringMap<PropertyPair*>::UT_Cursor cp1(m_pProperties);
853 		UT_GenericStringMap<PropertyPair*>::UT_Cursor cp2(pMatch->m_pProperties);
854 
855 		const PropertyPair* v1 = cp1.first();
856 		const PropertyPair* v2 = cp2.first();
857 
858 		do
859 		{
860 			const gchar *l1 = cp1.key().c_str();
861 			const gchar *l2 = cp2.key().c_str();
862 
863 			if (strcmp(l1, l2) != 0)
864 				return false;
865 
866 			l1 = v1->first;
867 			l2 = v2->first;
868 
869 			if (strcmp(l1,l2) != 0)
870 				return false;
871 
872 			v1 = cp1.next();
873 			v2 = cp2.next();
874 		} while (cp1.is_valid() && cp2.is_valid());
875 
876 	#ifdef PT_TEST
877 		s_Matches++;
878 	#endif
879 	}
880 
881 	return true;
882 }
883 
884 
885 /*! Create a new AttrProp with exactly the attributes/properties given.
886   \return NULL on failure, the newly-created PP_AttrProp.
887 */
createExactly(const gchar ** attributes,const gchar ** properties) const888 PP_AttrProp * PP_AttrProp::createExactly(const gchar ** attributes,
889 					 const gchar ** properties) const
890 {
891 	// first, create a new AttrProp using just the values given.
892 
893 	PP_AttrProp * papNew = new PP_AttrProp();
894 	if (!papNew)
895 		goto Failed;
896 	if (!papNew->setAttributes(attributes) || !papNew->setProperties(properties))
897 		goto Failed;
898 	return papNew;
899 
900 Failed:
901 	DELETEP(papNew);
902 	return NULL;
903 }
904 
905 /*! Create a new AttrProp based upon the given one, adding or replacing the items given.
906 	 \return NULL on failure, the newly-created PP_AttrProp clone otherwise.
907 */
cloneWithReplacements(const gchar ** attributes,const gchar ** properties,bool bClearProps) const908 PP_AttrProp * PP_AttrProp::cloneWithReplacements(const gchar ** attributes,
909 												 const gchar ** properties,
910 												 bool bClearProps) const
911 {
912 	bool bIgnoreProps = false; // see below
913 
914 	// first, create a new AttrProp using just the values given.
915 
916 	PP_AttrProp * papNew = new PP_AttrProp();
917 	if (!papNew)
918 		goto Failed;
919 	if (!papNew->setAttributes(attributes) || !papNew->setProperties(properties))
920 		goto Failed;
921 
922 	// next, add any items that we have that are not present
923 	// (have not been overridden) in the new one.
924 
925 	UT_uint32 k;
926 	const gchar * n;
927 	const gchar * v;
928 	const gchar * vNew;
929 
930 	k = 0;
931 	while (getNthAttribute(k++,n,v))
932 	{
933 		// TODO decide if/whether to allow PT_PROPS_ATTRIBUTE_NAME here.
934 		// TODO The issue is: we use it to store the CSS properties and
935 		// TODO when we see it, we expand the value into one or more
936 		// TODO properties.  if we allow it to be given here, should
937 		// TODO we blowaway all of the existing properties and create
938 		// TODO them from this?  or should we expand it and override
939 		// TODO individual properties?
940 		// TODO for now, we just ignore it.
941 	    if (strcmp(n,PT_PROPS_ATTRIBUTE_NAME) == 0)
942 	    {
943 		UT_ASSERT_HARMLESS(UT_SHOULD_NOT_HAPPEN);
944 		continue;
945 	    }
946 	    if (!papNew->getAttribute(n,vNew))
947 	    {
948 		bool bres = papNew->setAttribute(n,v);
949 		if (!bres)
950 		    goto Failed;
951 	    }
952 	}
953 
954 	// we want to be able to remove all properties by setting the
955 	// props attribute to ""; in order for that to work, we need to
956 	// skip the following loop if props is set to ""
957 	const gchar * szValue;
958 
959 	if(papNew->getAttribute("props", szValue) && !*szValue)
960 		bIgnoreProps = true;
961 
962 	if (!bClearProps && !bIgnoreProps)
963 	{
964 		k = 0;
965 		while (getNthProperty(k++,n,v))
966 		{
967 			if (!papNew->getProperty(n,vNew))
968 				if (!papNew->setProperty(n,v))
969 					goto Failed;
970 		}
971 	}
972 
973 	// the following will remove all properties set to ""; this allows us
974 	// to remove properties by setting them to ""
975 	papNew->_clearEmptyProperties();
976 	papNew->_clearEmptyAttributes();
977 
978 	return papNew;
979 
980 Failed:
981 	DELETEP(papNew);
982 	return NULL;
983 }
984 
985 /// This function will remove all properties that are set to ""
_clearEmptyProperties()986 void PP_AttrProp::_clearEmptyProperties()
987 {
988 	if(!m_pProperties)
989 		return;
990 
991 	UT_GenericStringMap<PropertyPair*>::UT_Cursor _hc1(m_pProperties);
992 	PropertyPair * pEntry;
993 
994 	for ( pEntry  = _hc1.first(); _hc1.is_valid(); pEntry = _hc1.next())
995 	{
996 		if (pEntry)
997 		{
998 			const PropertyPair* p = pEntry;
999 
1000 			const char *s = p->first;
1001 			if(s == NULL || *s == 0)
1002 			{
1003 
1004 				gchar* tmp = const_cast<gchar*>(p->first);
1005 				UT_return_if_fail (!m_bIsReadOnly);
1006 				FREEP(tmp);
1007 				m_pProperties->remove(_hc1.key(),pEntry);
1008 				if (p->second)
1009 				{
1010 					delete p->second;
1011 				}
1012 				delete p;
1013 
1014 			}
1015 		}
1016 	}
1017 }
1018 
1019 /// This method clears both empty attributes and empty properties from [this] AP.
prune()1020 void PP_AttrProp::prune()
1021 {
1022 	_clearEmptyAttributes();
1023 	_clearEmptyProperties();
1024 }
1025 
1026 /// This function will remove all attributes that are equal to "" (*<gchar *> == NULL)
_clearEmptyAttributes()1027 void PP_AttrProp::_clearEmptyAttributes()
1028 {
1029 	if(!m_pAttributes)
1030 		return;
1031 
1032 	UT_GenericStringMap<gchar*>::UT_Cursor _hc1(m_pAttributes);
1033 	gchar * pEntry;
1034 
1035 	for ( pEntry  = _hc1.first(); _hc1.is_valid(); pEntry = _hc1.next())
1036 	{
1037 		if (pEntry && !*pEntry)
1038 		{
1039 			UT_return_if_fail (!m_bIsReadOnly);
1040 			m_pAttributes->remove(_hc1.key(),pEntry);
1041 			FREEP(pEntry);
1042 		}
1043 	}
1044 }
1045 
1046 /*! Create a new AttrProp based upon the given one, removing the items given
1047 	 regardless of their value.  See also PP_AttrProp::cloneWithEliminationIfEqual,
1048 	 which does similarly but only removes the items given if their value is equal
1049 	 to the value given.
1050 	 \return NULL on failure, the newly-created PP_AttrProp clone otherwise.
1051 */
cloneWithElimination(const gchar ** attributes,const gchar ** properties) const1052 PP_AttrProp * PP_AttrProp::cloneWithElimination(const gchar ** attributes,
1053 												const gchar ** properties) const
1054 {
1055 
1056 	// first, create an empty AttrProp.
1057 	PP_AttrProp * papNew = new PP_AttrProp();
1058 	if (!papNew)
1059 		goto Failed;
1060 
1061 	UT_uint32 k;
1062 	const gchar * n;
1063 	const gchar * v;
1064 
1065 	k = 0;
1066 	while (getNthAttribute(k++,n,v))
1067 	{
1068 		// for each attribute in the old set, add it to the
1069 		// new set only if it is not present in the given array.
1070 
1071 		if (attributes && *attributes)
1072 		{
1073 			const gchar ** p = attributes;
1074 			while (*p)
1075 			{
1076 				UT_return_val_if_fail (strcmp(p[0],PT_PROPS_ATTRIBUTE_NAME)!=0, NULL); // cannot handle PROPS here
1077 				if (strcmp(n,p[0])==0)		// found it, so we don't put it in the result.
1078 					goto DoNotIncludeAttribute;
1079 				p += 2;								// skip over value
1080 			}
1081 		}
1082 
1083 		// we didn't find it in the given array, add it to the new set.
1084 
1085 		if (!papNew->setAttribute(n,v))
1086 			goto Failed;
1087 
1088 	DoNotIncludeAttribute:
1089 		;
1090 	}
1091 
1092 	k = 0;
1093 	while (getNthProperty(k++,n,v))
1094 	{
1095 		// for each property in the old set, add it to the
1096 		// new set only if it is not present in the given array.
1097 
1098 		if (properties && *properties)
1099 		{
1100 			const gchar ** p = properties;
1101 			while (*p)
1102 			{
1103 				if (strcmp(n,p[0])==0)		// found it, so we don't put it in the result.
1104 					goto DoNotIncludeProperty;
1105 				p += 2;
1106 			}
1107 		}
1108 
1109 		// we didn't find it in the given array, add it to the new set.
1110 
1111 		if (!papNew->setProperty(n,v))
1112 			goto Failed;
1113 
1114 	DoNotIncludeProperty:
1115 		;
1116 	}
1117 
1118 	return papNew;
1119 
1120 Failed:
1121 	DELETEP(papNew);
1122 	return NULL;
1123 }
1124 
1125 /*! After the construction of a new AP, use this method to mark it read-only.
1126 	 It returns if the AP is already read-only; otherwise it marks [this] AP
1127 	 as read-only and then computes its checksum in order to speed subsequent
1128 	 equivalence testing.  There is no return value.
1129 */
markReadOnly(void)1130 void PP_AttrProp::markReadOnly(void)
1131 {
1132 	UT_return_if_fail (!m_bIsReadOnly);
1133 	m_bIsReadOnly = true;
1134 	_computeCheckSum();
1135 }
1136 
1137 /*! Create a new AttrProp based upon the given one, removing the items given
1138 	 if their value is equal to that given.  See also PP_AttrProp::cloneWithElimination,
1139 	 which does similarly but removes the items given regardless of whether or not
1140 	 their value is equal to the value given.
1141 	 \return NULL on failure, the newly-created PP_AttrProp clone otherwise.
1142 */
cloneWithEliminationIfEqual(const gchar ** attributes,const gchar ** properties) const1143 PP_AttrProp * PP_AttrProp::cloneWithEliminationIfEqual(const gchar ** attributes,
1144 												const gchar ** properties) const
1145 {
1146 	// first, create an empty AttrProp.
1147 	PP_AttrProp * papNew = new PP_AttrProp();
1148 	if (!papNew)
1149 		goto Failed;
1150 
1151 	UT_uint32 k;
1152 	const gchar * n;
1153 	const gchar * v;
1154 
1155 	k = 0;
1156 	while (getNthAttribute(k++,n,v))
1157 	{
1158 		// for each attribute in the old set, add it to the
1159 		// new set only if it is not present in the given array.
1160 
1161 		if (attributes && *attributes)
1162 		{
1163 			const gchar ** p = attributes;
1164 			while (*p)
1165 			{
1166 				if(strcmp(p[0],PT_PROPS_ATTRIBUTE_NAME)!=0)
1167 					goto DoNotIncludeAttribute; // cannot handle PROPS here
1168 				if (strcmp(n,p[0])==0 && strcmp(n,p[1])==0)		// found it, so we don't put it in the result.
1169 					goto DoNotIncludeAttribute;
1170 				p += 2;								// skip over value
1171 			}
1172 		}
1173 
1174 		// we didn't find it in the given array, add it to the new set.
1175 
1176 		if (!papNew->setAttribute(n,v))
1177 			goto Failed;
1178 
1179 	DoNotIncludeAttribute:
1180 		;
1181 	}
1182 
1183 	k = 0;
1184 	while (getNthProperty(k++,n,v))
1185 	{
1186 		// for each property in the old set, add it to the
1187 		// new set only if it is not present in the given array.
1188 
1189 		if (properties && *properties)
1190 		{
1191 			const gchar ** p = properties;
1192 			while (*p)
1193 			{
1194 				if (strcmp(n,p[0])==0 && strcmp(n,p[1])==0)		// found it, so we don't put it in the result.
1195 					goto DoNotIncludeProperty;
1196 				p += 2;
1197 			}
1198 		}
1199 
1200 		// we didn't find it in the given array, add it to the new set.
1201 
1202 		if (!papNew->setProperty(n,v))
1203 			goto Failed;
1204 
1205 	DoNotIncludeProperty:
1206 		;
1207 	}
1208 
1209 	return papNew;
1210 
1211 Failed:
1212 	DELETEP(papNew);
1213 	return NULL;
1214 }
1215 
1216 /*! (?)TODO: PLEASE DOCUMENT ME!
1217 */
hashcodeBytesAP(UT_uint32 init,const void * pv,UT_uint32 cb)1218 static UT_uint32 hashcodeBytesAP(UT_uint32 init, const void * pv, UT_uint32 cb)
1219 {
1220  	// modified from ut_string_class.cpp's hashcode() which got it from glib
1221  	UT_uint32 h = init;
1222  	const unsigned char * pb = static_cast<const unsigned char *>(pv);
1223 
1224  	if (cb)
1225  	{
1226  		// for AP data, limit hash to consume at most 8 bytes
1227  		if (cb > 8) { cb = 8; }
1228 
1229  		for (; cb != 0; pb += 1, cb -= 1)
1230  		{
1231  			h = (h << 5) - h + *pb;
1232  		}
1233  	}
1234 
1235  	return h;
1236 }
1237 
1238 /*! Compute the checksum by which we speed AP equivalence testing.  (?)To the best of my knowledge,
1239 	 collision is still possible.  (?)TODO: Document the algorithm/process here, and
1240 	 answer remaining questions about this chunk of code.
1241 */
_computeCheckSum(void)1242 void PP_AttrProp::_computeCheckSum(void)
1243 {
1244 	m_checkSum = 0;
1245 
1246  	if (!m_pAttributes && !m_pProperties)
1247   		return;
1248 
1249 	const gchar *s1, *s2;
1250  	UT_uint32	cch = 0;
1251  	gchar	*rgch;
1252 
1253 	rgch = NULL;
1254  	if (m_pAttributes)
1255   	{
1256  		UT_GenericStringMap<gchar*>::UT_Cursor c1(m_pAttributes);
1257  		const gchar *val = c1.first();
1258 
1259  		while (val != NULL)
1260  		{
1261  			s1 = c1.key().c_str();
1262  			s2 = val;
1263 
1264  			cch = strlen(s1);
1265 
1266  			m_checkSum = hashcodeBytesAP(m_checkSum, s1, cch);
1267 
1268  			cch = strlen(s2);
1269 
1270 			rgch = g_ascii_strdown(s2, 9);
1271 			rgch[8] = '\0';
1272  			m_checkSum = hashcodeBytesAP(m_checkSum, rgch, cch);
1273 			g_free (rgch); rgch = NULL;
1274 
1275  			if (!c1.is_valid())
1276  				break;
1277  			val = c1.next();
1278  		}
1279  	}
1280 
1281  	if (m_pProperties)
1282   	{
1283  		UT_GenericStringMap<PropertyPair*>::UT_Cursor c2(m_pProperties);
1284  		const PropertyPair *val;
1285 
1286  		val = c2.first();
1287  		while (val != NULL)
1288  		{
1289  			s1 = c2.key().c_str();
1290  			cch = strlen(s1);
1291 			rgch = g_ascii_strdown(s1, 9);
1292 			rgch[8] = '\0';
1293  			m_checkSum = hashcodeBytesAP(m_checkSum, rgch, cch);
1294 			g_free (rgch); rgch = NULL;
1295 
1296  			s2 = val->first;
1297  			cch = strlen(s2);
1298 			rgch = g_ascii_strdown(s2, 9);
1299 			rgch[8] = '\0';
1300  			m_checkSum = hashcodeBytesAP(m_checkSum, rgch, cch);
1301 			g_free (rgch); rgch = NULL;
1302 
1303  			if (!c2.is_valid())
1304  				break;
1305  			val = c2.next();
1306  		}
1307 	}
1308 	return;
1309 }
1310 
1311 /*! This is an accessor method that gets the checksum of [this] AP, by which we speed
1312 	 equivalence testing.  The speedup occurs when we bypass full-testing
1313 	 in the case of checksum mismatch when we know for sure that the APs are not
1314 	 equivalent.  Because collisions still occur, we do the full-testing if the
1315 	 checksums do match.
1316 	 \retval m_checkSum The unsigned 32-bit integer which serves as the AP's checksum.
1317 */
getCheckSum(void) const1318 UT_uint32 PP_AttrProp::getCheckSum(void) const
1319 {
1320   //
1321   // Why is this assert present? This is harmless.
1322   //	UT_ASSERT_HARMLESS(m_bIsReadOnly);
1323 	return m_checkSum;
1324 }
1325 
operator =(const PP_AttrProp & Other)1326 void PP_AttrProp::operator = (const PP_AttrProp &Other)
1327 {
1328 	UT_uint32 countMyAttrs = ((Other.m_pAttributes) ? Other.m_pAttributes->size() : 0);
1329 
1330 	UT_uint32 Index;
1331 	for(Index = 0; Index < countMyAttrs; Index++)
1332 	{
1333 		const gchar * szName;
1334 		const gchar * szValue;
1335 		if(Other.getNthAttribute(Index, szName, szValue))
1336 		{
1337 			setAttribute(szName, szValue);
1338 		}
1339 	}
1340 
1341 	UT_uint32 countMyProps = ((Other.m_pProperties) ? Other.m_pProperties->size() : 0);
1342 
1343 	for(Index = 0; Index < countMyProps; Index++)
1344 	{
1345 		const gchar * szName;
1346 		const gchar * szValue;
1347 		if(Other.getNthProperty(Index, szName, szValue))
1348 		{
1349 			setProperty(szName, szValue);
1350 		}
1351 	}
1352 
1353 }
1354 
getIndex(void) const1355 UT_uint32 PP_AttrProp::getIndex(void) const
1356 {
1357 	return m_index;
1358 }
1359 
setIndex(UT_uint32 i)1360 void PP_AttrProp::setIndex(UT_uint32 i)
1361 {
1362 	m_index = i;
1363 	if(m_szProperties)
1364 	{
1365 		delete [] m_szProperties;
1366 	}
1367 	m_szProperties = NULL;
1368 }
1369 
1370 /*! This method checks if [this] AP and the given AP are functionally equivalent.
1371     In contrast to is isExactMatch this function will return true if
1372     the attrs and props are same even if they are in different order;
1373     it is computationally much more involved than isExactMatch().
1374 	 On that note, it may be worth looking into putting some more explicit collision
1375 	 guarantees into the checksum computation algorithm, such to take advantage of
1376 	 those checksums here.  IOW, make it such to be trusted that if checksums match,
1377 	 APs are equivalent (but not necessarily exact matches).
1378 	 \retval TRUE if equivalent to given AP (regardless of order), FALSE otherwise.
1379 */
isEquivalent(const PP_AttrProp * pAP2) const1380 bool PP_AttrProp::isEquivalent(const PP_AttrProp * pAP2) const
1381 {
1382 	if(!pAP2)
1383 		return false;
1384 
1385 	if(   getAttributeCount() != pAP2->getAttributeCount()
1386 	   || getPropertyCount()  != pAP2->getPropertyCount())
1387 		return false;
1388 
1389 	UT_uint32 i;
1390 	const gchar * pName, * pValue, * pValue2;
1391 
1392 	for(i =  0; i < getAttributeCount(); ++i)
1393 	{
1394 		UT_return_val_if_fail(getNthAttribute(i,pName,pValue),false);
1395 
1396 		if(!pAP2->getAttribute(pName,pValue2))
1397 			return false;
1398 
1399 		// ignore property attribute
1400 		if(0 == strcmp(pValue, PT_PROPS_ATTRIBUTE_NAME))
1401 			continue;
1402 
1403 		// handle revision attribute correctly
1404 		if(0 == strcmp(pValue, PT_REVISION_ATTRIBUTE_NAME))
1405 		{
1406 			// requires special treatment
1407 			PP_RevisionAttr r1(pValue);
1408 			PP_RevisionAttr r2 (pValue2);
1409 
1410 			if(!(r1 == r2))
1411 			{
1412 				return false;
1413 			}
1414 		}
1415 		else if(0 != strcmp(pValue,pValue2))
1416 			return false;
1417 	}
1418 
1419 	for(i =  0; i < getPropertyCount(); ++i)
1420 	{
1421 		UT_return_val_if_fail(getNthProperty(i,pName,pValue),false);
1422 
1423 		if(!pAP2->getProperty(pName,pValue2))
1424 			return false;
1425 
1426 		if(0 != strcmp(pValue,pValue2))
1427 			return false;
1428 	}
1429 
1430 	return true;
1431 }
1432 
1433 /*! This method does the same as PP_AttrProp::isEquivalent(const PP_AttrProp *) except
1434 	 that instead of being passed another AP to be compared to, it is passed sets of
1435 	 attributes and properties which only virtually comprise another AP.
1436 	 \retval TRUE if equivalent (regardless of order) to given Attrs and Props, FALSE otherwise.
1437 */
isEquivalent(const gchar ** attrs,const gchar ** props) const1438 bool PP_AttrProp::isEquivalent(const gchar ** attrs, const gchar ** props) const
1439 {
1440 	UT_uint32 iAttrsCount  = 0;
1441 	UT_uint32 iPropsCount = 0;
1442 
1443 	const gchar ** p = attrs;
1444 
1445 	while(p && *p)
1446 	{
1447 		iAttrsCount++;
1448 		p += 2;
1449 	}
1450 
1451 	p = props;
1452 
1453 	while(p && *p)
1454 	{
1455 		iPropsCount++;
1456 		p += 2;
1457 	}
1458 
1459 
1460 	if(   getAttributeCount() != iAttrsCount
1461 	   || getPropertyCount()  != iPropsCount)
1462 		return false;
1463 
1464 	UT_uint32 i;
1465 	const gchar * pName, * pValue, * pValue2;
1466 
1467 	for(i =  0; i < getAttributeCount(); ++i)
1468 	{
1469 		pName = attrs[2*i];
1470 		pValue = attrs[2*i + 1];
1471 
1472 		if(!getAttribute(pName,pValue2))
1473 			return false;
1474 
1475 		// ignore property attribute
1476 		if(0 == strcmp(pValue, PT_PROPS_ATTRIBUTE_NAME))
1477 			continue;
1478 
1479 		// handle revision attribute correctly
1480 		if(0 == strcmp(pValue, PT_REVISION_ATTRIBUTE_NAME))
1481 		{
1482 			// requires special treatment
1483 			PP_RevisionAttr r1(pValue);
1484 			PP_RevisionAttr r2 (pValue2);
1485 
1486 			if(!(r1 == r2))
1487 			{
1488 				return false;
1489 			}
1490 		}
1491 		else if(0 != strcmp(pValue,pValue2))
1492 			return false;
1493 	}
1494 
1495 	for(i =  0; i < getPropertyCount(); ++i)
1496 	{
1497 		pName = props[2*i];
1498 		pValue = props[2*i + 1];
1499 
1500 		if(!getProperty(pName,pValue2))
1501 			return false;
1502 
1503 		if(0 != strcmp(pValue,pValue2))
1504 			return false;
1505 	}
1506 
1507 	return true;
1508 }
1509 
1510 /*! \fn bool PP_AttrProp::explodeStyle(const PD_Document * pDoc, bool bOverwrite)
1511 	 \brief This function transfers attributes and properties defined in style into the AP.
1512     \param bOverwrite indicates what happens if the property/attribute is already present. If false \
1513      (default) the style definition is ignored; if true the style value overrides the present value.
1514 */
explodeStyle(const PD_Document * pDoc,bool bOverwrite)1515 bool PP_AttrProp::explodeStyle(const PD_Document * pDoc, bool bOverwrite)
1516 {
1517 	UT_return_val_if_fail(pDoc,false);
1518 
1519 	// expand style if present
1520 	const gchar * pszStyle = NULL;
1521 	if(getAttribute(PT_STYLE_ATTRIBUTE_NAME, pszStyle))
1522 	{
1523 		PD_Style * pStyle = NULL;
1524 
1525         if(pszStyle && (strcmp(pszStyle, "None") != 0) && pDoc->getStyle(pszStyle,&pStyle))
1526         {
1527 			UT_Vector vAttrs;
1528 			UT_Vector vProps;
1529 
1530 			UT_sint32 i;
1531 
1532 			pStyle->getAllAttributes(&vAttrs, 100);
1533 			pStyle->getAllProperties(&vProps, 100);
1534 
1535 			for(i = 0; i < vProps.getItemCount(); i += 2)
1536 			{
1537 				const gchar * pName =  (const gchar *)vProps.getNthItem(i);
1538 				const gchar * pValue = (const gchar *)vProps.getNthItem(i+1);
1539 				const gchar * p;
1540 
1541 				bool bSet = bOverwrite || !getProperty(pName, p);
1542 
1543 				if(bSet)
1544 					setProperty(pName, pValue);
1545 			}
1546 
1547 			// attributes are more complicated, because there are some style attributes that must
1548 			// not be transferred to the generic AP
1549 			for(i = 0; i < vAttrs.getItemCount(); i += 2)
1550 			{
1551 				const gchar * pName = (const gchar *)vAttrs.getNthItem(i);
1552 				if(!pName || !strcmp(pName, "type")
1553 				          || !strcmp(pName, "name")
1554 				          || !strcmp(pName, "basedon")
1555 				          || !strcmp(pName, "followedby")
1556  				          || !strcmp(pName, "props"))
1557 				{
1558 					continue;
1559 				}
1560 
1561 				const gchar * pValue = (const gchar *)vAttrs.getNthItem(i+1);
1562 				const gchar * p;
1563 
1564 				bool bSet = bOverwrite || !getAttribute(pName, p);
1565 
1566 				if(bSet)
1567 					setAttribute(pName, pValue);
1568 			}
1569 		}
1570 	}
1571 
1572 	return true;
1573 }
1574 
1575 /*! This is a debugging tool which serves to dump in readable form (as UT_DEBUGMSGs)
1576 	 the contents of [this] AP.
1577 */
miniDump(const PD_Document * pDoc) const1578 void PP_AttrProp::miniDump(const PD_Document * pDoc) const
1579 {
1580 #ifdef DEBUG
1581 	const gchar * pName, * pValue;
1582 	UT_uint32 i = 0;
1583 
1584 	UT_DEBUGMSG(("--------------------- PP_AttrProp mini dump --------------------------------\n"));
1585 	UT_DEBUGMSG(("Attributes:\n"));
1586 	while(getNthAttribute(i,pName,pValue))
1587 	{
1588 		UT_DEBUGMSG(("%s : %s\n", pName, pValue));
1589 		++i;
1590 	}
1591 
1592 	UT_DEBUGMSG(("Properties:\n"));
1593 	i = 0;
1594 	while(getNthProperty(i,pName,pValue))
1595 	{
1596 		UT_DEBUGMSG(("%s : %s\n", pName, pValue));
1597 		++i;
1598 	}
1599 
1600 	if(m_iRevisedIndex != 0xffffffff && pDoc)
1601 	{
1602 		UT_DEBUGMSG(("Attached revised AP:\n"));
1603 		const PP_AttrProp * pRevAP;
1604 		pDoc->getAttrProp(m_iRevisedIndex, const_cast<const PP_AttrProp **>(&pRevAP));
1605 
1606 		// avoid endless loop on circular reference
1607 		if(pRevAP && pRevAP->getRevisedIndex() != m_iRevisedIndex)
1608 			pRevAP->miniDump(pDoc);
1609 	}
1610 
1611 	UT_DEBUGMSG(("----------------------------------------------------------------------------\n"));
1612 #else
1613 	UT_UNUSED(pDoc);
1614 #endif
1615 }
1616