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