1 /*
2  * Copyright (C) 2001-2003 Peter J Jones (pjones@pmade.org)
3  * All Rights Reserved
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in
13  *    the documentation and/or other materials provided with the
14  *    distribution.
15  * 3. Neither the name of the Author nor the names of its contributors
16  *    may be used to endorse or promote products derived from this software
17  *    without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS''
20  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
22  * PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR
23  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
26  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
27  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
29  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  */
32 
33 /*
34  * $Id: ait_impl.cpp 457246 2015-01-21 17:09:53Z satskyse $
35  * NOTE: This file was modified from its original version 0.6.0
36  *       to fit the NCBI C++ Toolkit build framework and
37  *       API and functionality requirements.
38  * Most importantly, it adds support for XML namespaces (see "namespace.hpp").
39  */
40 
41 /** @file
42  * This file implements the ait_impl, xml::attributes::iterator,
43  * xml::attributes::const_iterator and xml::attributes::attr classes.
44 **/
45 
46 // xmlwrapp includes
47 #include "ait_impl.hpp"
48 #include "deref_impl.hpp"
49 #include "utility.hpp"
50 #include <misc/xmlwrapp/attributes.hpp>
51 #include <misc/xmlwrapp/exception.hpp>
52 
53 // standard includes
54 #include <algorithm>
55 #include <stdexcept>
56 #include <string.h>
57 
58 // libxml2 includes
59 #include <libxml/tree.h>
60 
61 using namespace xml;
62 using namespace xml::impl;
63 
64 
65 const char*     kDerefError = "dereferencing non initialised or out of range iterator";
66 const char*     kRefError   = "referencing non initialised or out of range iterator";
67 const char*     kAdvError   = "advancing non initialised or out of range iterator";
68 const char*     kInvalidDefaultIterError = "invalid default attribute iterator";
69 
70 
71 /*
72  * First we have the ait_impl class.
73  */
74 
75 //####################################################################
ait_impl(xmlNodePtr node,xmlAttrPtr prop,bool from_find)76 ait_impl::ait_impl (xmlNodePtr node, xmlAttrPtr prop, bool from_find) :
77     from_find_(from_find) {
78     attr_.set_data(node, prop, false);
79 }
80 //####################################################################
ait_impl(xmlNodePtr node,phantom_attr * prop,bool from_find)81 ait_impl::ait_impl (xmlNodePtr node, phantom_attr* prop, bool from_find) :
82     from_find_(from_find) {
83     attr_.set_data(node, prop, true);
84 }
85 //####################################################################
ait_impl(const ait_impl & other)86 ait_impl::ait_impl (const ait_impl &other) :
87     attr_(other.attr_),
88     from_find_(other.from_find_)
89 {}
90 //####################################################################
operator =(const ait_impl & other)91 ait_impl& ait_impl::operator= (const ait_impl &other) {
92     ait_impl tmp(other);
93 
94     attr_.swap(tmp.attr_);
95     std::swap(from_find_, tmp.from_find_);
96     return *this;
97 }
98 //####################################################################
get(void)99 xml::attributes::attr* ait_impl::get (void) {
100     return &attr_;
101 }
102 //####################################################################
operator ++(void)103 ait_impl& ait_impl::operator++ (void) {
104     if (from_find_)
105         throw xml::exception("cannot iterate using iterators "
106                              "produced by find(...) methods");
107 
108     // Here: the iterator cannot point to a default attribute because
109     //       it is not produced by find(...) methods
110     if (attr_.prop_)
111         attr_.prop_ = static_cast<xmlAttrPtr>(attr_.prop_)->next;
112     else
113         throw xml::exception(kAdvError);
114     return *this;
115 }
116 //####################################################################
operator ++(int)117 ait_impl ait_impl::operator++ (int) {
118     ait_impl tmp(*this);
119     ++(*this);
120     return tmp;
121 }
122 //####################################################################
123 
124 /*
125  * Member functions for the xml::attributes::iterator class.
126  */
127 
128 //####################################################################
iterator(void)129 xml::attributes::iterator::iterator (void) {
130     pimpl_ = new ait_impl(0, static_cast<xmlAttrPtr>(0), false);
131 }
132 //####################################################################
iterator(void * node,void * prop,bool def_prop,bool from_find)133 xml::attributes::iterator::iterator (void *node, void *prop,
134                                      bool def_prop, bool from_find) {
135     if (def_prop)
136         pimpl_ = new ait_impl(static_cast<xmlNodePtr>(node),
137                               static_cast<phantom_attr*>(prop), from_find);
138     else
139         pimpl_ = new ait_impl(static_cast<xmlNodePtr>(node),
140                               static_cast<xmlAttrPtr>(prop), from_find);
141 }
142 //####################################################################
iterator(const iterator & other)143 xml::attributes::iterator::iterator (const iterator &other) {
144     pimpl_ = new ait_impl(*other.pimpl_);
145 }
146 //####################################################################
147 xml::attributes::iterator&
operator =(const iterator & other)148 xml::attributes::iterator::operator= (const iterator &other) {
149     iterator tmp(other);
150     swap(tmp);
151     return *this;
152 }
153 //####################################################################
swap(iterator & other)154 void xml::attributes::iterator::swap (iterator &other) {
155     std::swap(pimpl_, other.pimpl_);
156 }
157 //####################################################################
~iterator(void)158 xml::attributes::iterator::~iterator (void) {
159     delete pimpl_;
160 }
161 //####################################################################
162 xml::attributes::iterator::reference
operator *(void) const163 xml::attributes::iterator::operator* (void) const {
164     xml::attributes::attr*  att = pimpl_->get();
165     if (att->normalize())
166         return * static_cast<xml::attributes::attr*>
167                             (get_ptr_to_attr_instance(att));
168     throw xml::exception(kDerefError);
169 }
170 //####################################################################
171 xml::attributes::iterator::pointer
operator ->(void) const172 xml::attributes::iterator::operator-> (void) const {
173     xml::attributes::attr*  att = pimpl_->get();
174     if (att->normalize())
175         return static_cast<xml::attributes::attr*>
176                             (get_ptr_to_attr_instance(att));
177     throw xml::exception(kRefError);
178 }
179 //####################################################################
operator ++(void)180 xml::attributes::iterator& xml::attributes::iterator::operator++ (void) {
181     ++(*pimpl_);
182     return *this;
183 }
184 //####################################################################
operator ++(int)185 xml::attributes::iterator xml::attributes::iterator::operator++ (int) {
186     iterator tmp(*this);
187     ++(*this);
188     return tmp;
189 }
190 //####################################################################
191 
192 /*
193  * Member functions for the xml::attributes::const_iterator class.
194  */
195 
196 //####################################################################
const_iterator(void)197 xml::attributes::const_iterator::const_iterator (void) {
198     pimpl_ = new ait_impl(0, static_cast<xmlAttrPtr>(0), false);
199 }
200 //####################################################################
const_iterator(void * node,void * prop,bool def_prop,bool from_find)201 xml::attributes::const_iterator::const_iterator (void *node, void *prop,
202                                                  bool def_prop,
203                                                  bool from_find) {
204     if (def_prop)
205         pimpl_ = new ait_impl(static_cast<xmlNodePtr>(node),
206                               static_cast<phantom_attr*>(prop), from_find);
207     else
208         pimpl_ = new ait_impl(static_cast<xmlNodePtr>(node),
209                               static_cast<xmlAttrPtr>(prop), from_find);
210 }
211 //####################################################################
const_iterator(const const_iterator & other)212 xml::attributes::const_iterator::const_iterator (const const_iterator &other) {
213     pimpl_ = new ait_impl(*other.pimpl_);
214 }
215 //####################################################################
const_iterator(const iterator & other)216 xml::attributes::const_iterator::const_iterator (const iterator &other) {
217     pimpl_ = new ait_impl(*other.pimpl_);
218 }
219 //####################################################################
220 xml::attributes::const_iterator&
operator =(const const_iterator & other)221 xml::attributes::const_iterator::operator= (const const_iterator &other) {
222     const_iterator tmp(other);
223     swap(tmp);
224     return *this;
225 }
226 //####################################################################
swap(const_iterator & other)227 void xml::attributes::const_iterator::swap (const_iterator &other) {
228     std::swap(pimpl_, other.pimpl_);
229 }
230 //####################################################################
~const_iterator(void)231 xml::attributes::const_iterator::~const_iterator (void) {
232     delete pimpl_;
233 }
234 //####################################################################
235 xml::attributes::const_iterator::reference
operator *(void) const236 xml::attributes::const_iterator::operator* (void) const {
237     xml::attributes::attr*  att = pimpl_->get();
238     if (att->normalize())
239         return * static_cast<xml::attributes::attr*>
240                             (get_ptr_to_attr_instance(att));
241     throw xml::exception(kDerefError);
242 }
243 //####################################################################
244 xml::attributes::const_iterator::pointer
operator ->(void) const245 xml::attributes::const_iterator::operator-> (void) const {
246     xml::attributes::attr*  att = pimpl_->get();
247     if (att->normalize())
248         return static_cast<xml::attributes::attr*>
249                             (get_ptr_to_attr_instance(att));
250     throw xml::exception(kRefError);
251 }
252 //####################################################################
253 xml::attributes::const_iterator&
operator ++(void)254 xml::attributes::const_iterator::operator++ (void) {
255     ++(*pimpl_);
256     return *this;
257 }
258 //####################################################################
259 xml::attributes::const_iterator
operator ++(int)260 xml::attributes::const_iterator::operator++ (int) {
261     const_iterator tmp(*this);
262     ++(*this);
263     return tmp;
264 }
265 //####################################################################
266 
267 /*
268  * Member functions for the xml::attributes::attr class.
269  */
270 
271 //####################################################################
attr(void)272 xml::attributes::attr::attr (void) : xmlnode_(0), prop_(0), phantom_prop_(0)
273 {}
274 //####################################################################
attr(const attr & other)275 xml::attributes::attr::attr (const attr &other) :
276     xmlnode_(other.xmlnode_), prop_(other.prop_),
277     phantom_prop_(other.phantom_prop_), value_(other.value_)
278 {}
279 //####################################################################
operator =(const attr & other)280 xml::attributes::attr& xml::attributes::attr::operator= (const attr &other) {
281     attr tmp(other);
282     swap(tmp);
283     return *this;
284 }
285 //####################################################################
swap(attr & other)286 void xml::attributes::attr::swap (attr &other) {
287     std::swap(xmlnode_, other.xmlnode_);
288     std::swap(prop_, other.prop_);
289     std::swap(phantom_prop_, other.phantom_prop_);
290     value_.swap(other.value_);
291 }
292 //####################################################################
set_data(void * node,void * prop,bool def_prop)293 void xml::attributes::attr::set_data (void *node, void *prop, bool def_prop) {
294     xmlnode_ = node;
295     value_.erase();
296     if (def_prop) {
297         prop_ = 0;
298         phantom_prop_ = prop;
299     }
300     else {
301         prop_ = prop;
302         phantom_prop_ = 0;
303     }
304 }
305 //####################################################################
normalize(void) const306 void * xml::attributes::attr::normalize (void) const {
307     if (!xmlnode_)  return NULL;
308     if (prop_)      return prop_;
309 
310     if (phantom_prop_) {
311         if (static_cast<phantom_attr*>(phantom_prop_)->prop_)
312             return static_cast<phantom_attr*>(phantom_prop_)->prop_;
313         if (static_cast<phantom_attr*>(phantom_prop_)->def_prop_)
314             return static_cast<phantom_attr*>(phantom_prop_)->def_prop_;
315     }
316     return NULL;
317 }
318 //####################################################################
get_node(void) const319 void * xml::attributes::attr::get_node(void) const {
320     return xmlnode_;
321 }
322 //####################################################################
operator ==(const xml::attributes::attr & other) const323 bool xml::attributes::attr::operator==
324 (const xml::attributes::attr &  other) const {
325     return normalize() == other.normalize();
326 }
327 //####################################################################
is_default(void) const328 bool xml::attributes::attr::is_default (void) const {
329     if (phantom_prop_ && (!static_cast<phantom_attr*>(phantom_prop_)->prop_))
330         return true;
331     return false;
332 }
333 //####################################################################
get_name(void) const334 const char* xml::attributes::attr::get_name (void) const {
335     if (is_default()) {
336         xmlAttributePtr  dprop = static_cast<phantom_attr*>(phantom_prop_)->def_prop_;
337         if (!dprop)
338             throw xml::exception(kInvalidDefaultIterError);
339         return reinterpret_cast<const char*>(dprop->name);
340     }
341     if (prop_) {
342         const xmlChar *  name = static_cast<xmlAttrPtr>(prop_)->name;
343         return reinterpret_cast<const char*>(name);
344     }
345     const xmlChar *  name = static_cast<phantom_attr*>(phantom_prop_)->prop_->name;
346     return reinterpret_cast<const char*>(name);
347 }
348 //####################################################################
get_value(void) const349 const char* xml::attributes::attr::get_value (void) const {
350     if (is_default()) {
351         xmlAttributePtr  dprop = static_cast<phantom_attr*>(phantom_prop_)->def_prop_;
352         if (!dprop)
353             throw xml::exception(kInvalidDefaultIterError);
354         if (dprop->defaultValue)
355             return reinterpret_cast<const char*>(dprop->defaultValue);
356         return "";
357     }
358 
359     xmlChar *tmpstr;
360     if (prop_) {
361         tmpstr = xmlNodeListGetString(reinterpret_cast<xmlNodePtr>(xmlnode_)->doc,
362                                       reinterpret_cast<xmlAttrPtr>(prop_)->children,
363                                       1);
364     }
365     else {
366         tmpstr = xmlNodeListGetString(reinterpret_cast<xmlNodePtr>(xmlnode_)->doc,
367                                       reinterpret_cast<phantom_attr*>(phantom_prop_)->prop_->children,
368                                       1);
369     }
370     if (tmpstr == 0)
371         return "";
372 
373     xmlchar_helper helper(tmpstr);
374     value_.assign(reinterpret_cast<const char*>(tmpstr));
375     return value_.c_str();
376 }
377 //####################################################################
378 xml::ns
get_namespace(xml::ns::ns_safety_type type) const379 xml::attributes::attr::get_namespace (xml::ns::ns_safety_type type) const {
380     xmlNs *     definition;
381     if (is_default()) {
382         definition = static_cast<xmlNs*>(resolve_default_attr_ns());
383     }
384     else {
385         xmlAttrPtr  prop = reinterpret_cast<xmlAttrPtr>(prop_);
386         if (!prop_)
387             prop = reinterpret_cast<phantom_attr*>(phantom_prop_)->prop_;
388         definition = prop->ns;
389     }
390 
391     if (type == xml::ns::type_safe_ns) {
392         return definition
393             ? xml::ns(reinterpret_cast<const char*>(definition->prefix),
394                       reinterpret_cast<const char*>(definition->href))
395             : xml::ns(xml::ns::type_void);
396     }
397     // unsafe namespace
398     return xml::attributes::createUnsafeNamespace(definition);
399 }
400 //####################################################################
401 // Resolves a namespace for a default attribute.
402 // If it cannot resolve it then an exception is thrown.
403 // NULL is returned if no namespace should be used.
resolve_default_attr_ns(void) const404 void *xml::attributes::attr::resolve_default_attr_ns (void) const {
405     if (!is_default())
406         throw xml::exception("internal library error: "
407                              "resolving non-default attribute namespace");
408 
409     xmlAttributePtr  dprop = static_cast<phantom_attr*>(phantom_prop_)->def_prop_;
410     if (!dprop)
411         throw xml::exception(kInvalidDefaultIterError);
412     xmlNs *  definition(xmlSearchNs(NULL, reinterpret_cast<xmlNode*>(xmlnode_),
413                                           dprop->prefix));
414     if (dprop->prefix)
415         if (!definition)
416             throw xml::exception("cannot resolve default attribute namespace");
417     return definition;
418 }
419 //####################################################################
420 // This converts a default attribute to a regular one
convert(void)421 void xml::attributes::attr::convert (void) {
422     if (!is_default())
423         return;
424 
425     xmlNs * definition = static_cast<xmlNs*>(resolve_default_attr_ns());
426     xmlAttributePtr  dprop = static_cast<phantom_attr*>(phantom_prop_)->def_prop_;
427     if (!dprop)
428         throw xml::exception(kInvalidDefaultIterError);
429     xmlAttrPtr new_attr = xmlSetNsProp(reinterpret_cast<xmlNode*>(xmlnode_),
430                                        definition,
431                                        dprop->name,
432                                        dprop->defaultValue);
433     if (!new_attr)
434         throw xml::exception("cannot convert default attribute into a regular one");
435 
436     // It's not a default any more
437     static_cast<phantom_attr*>(phantom_prop_)->def_prop_ = NULL;
438     static_cast<phantom_attr*>(phantom_prop_)->prop_ = new_attr;
439     return;
440 }
441 //####################################################################
set_value(const char * value)442 void xml::attributes::attr::set_value (const char*  value) {
443     convert();
444     xmlAttrPtr prop = reinterpret_cast<xmlAttrPtr>(normalize());
445     xmlSetNsProp(reinterpret_cast<xmlNode*>(xmlnode_),
446                  prop->ns,
447                  prop->name,
448                  reinterpret_cast<const xmlChar*>(value));
449     return;
450 }
451 //####################################################################
erase_namespace(void)452 void xml::attributes::attr::erase_namespace (void) {
453     convert();
454     xmlAttrPtr prop = reinterpret_cast<xmlAttrPtr>(normalize());
455     prop->ns = NULL;
456 }
457 //####################################################################
set_namespace(const char * prefix)458 xml::ns xml::attributes::attr::set_namespace (const char *prefix) {
459     if (!prefix || prefix[0] == '\0') {
460         erase_namespace();
461         return xml::attributes::createUnsafeNamespace(NULL);
462     }
463 
464     convert();
465     xmlAttrPtr prop = reinterpret_cast<xmlAttrPtr>(normalize());
466     xmlNs *  definition(xmlSearchNs(NULL, reinterpret_cast<xmlNode*>(xmlnode_),
467                                           reinterpret_cast<const xmlChar*>(prefix)));
468     if (!definition)
469         throw xml::exception("Namespace definition is not found");
470     prop->ns = definition;
471     return xml::attributes::createUnsafeNamespace(definition);
472 }
473 //####################################################################
set_namespace(const xml::ns & name_space)474 xml::ns xml::attributes::attr::set_namespace (const xml::ns &name_space) {
475     if (name_space.is_void()) {
476         erase_namespace();
477         return xml::attributes::createUnsafeNamespace(NULL);
478     }
479 
480     convert();
481     xmlAttrPtr prop = reinterpret_cast<xmlAttrPtr>(normalize());
482 
483     if (!name_space.is_safe()) {
484         prop->ns = reinterpret_cast<xmlNs*>(getUnsafeNamespacePointer(name_space));
485     }
486     else {
487         const char *    prefix(name_space.get_prefix());
488         if (prefix[0] == '\0')
489             prefix = NULL;
490         xmlNs *  definition(xmlSearchNs(NULL, reinterpret_cast<xmlNode*>(xmlnode_),
491                                               reinterpret_cast<const xmlChar*>(prefix)));
492         if (!definition)
493             throw xml::exception("Namespace definition is not found");
494         if (!xmlStrEqual(definition->href, reinterpret_cast<const xmlChar*>(name_space.get_uri())))
495             throw xml::exception("Namespace definition URI differs to the given");
496         prop->ns = definition;
497     }
498     return createUnsafeNamespace(prop->ns);
499 }
500 //####################################################################
501 
502 /*
503  * Now some friend functions
504  */
505 
506 //####################################################################
507 namespace xml {
508 
509 namespace impl {
510     //####################################################################
find_prop(xmlNodePtr xmlnode,const char * name,const ns * nspace)511     xmlAttrPtr find_prop (xmlNodePtr xmlnode,
512                           const char *name, const ns *nspace) {
513 
514         // The similar check is in libxml2 static function
515         // xmlGetPropNodeInternal(...)
516         if ( (xmlnode == NULL) ||
517              (xmlnode->type != XML_ELEMENT_NODE) ||
518              (name == NULL) )
519             return NULL;
520 
521         const ns *      ns_to_match = nspace;
522         const char *    name_to_match = name;
523 
524         // Check first if the name is qualified
525         const char *  column = strchr(name, ':');
526 
527         if (column) {
528             if (nspace)
529                 return NULL;    // Both namespace and the name is qualified
530 
531             if (column == name)
532                 return NULL;    // The name starts with :
533 
534             if (*(column + 1) == '\0')
535                 return NULL;    // No attribute name is given
536 
537             std::string prefix(name, column - name);
538             xmlNsPtr  resolved_ns = xmlSearchNs(xmlnode->doc,
539                                                 xmlnode,
540                                                 reinterpret_cast<const xmlChar*>(prefix.c_str()));
541             if (!resolved_ns)
542                 return NULL;    // No such namespace found
543 
544             name_to_match = column + 1;
545             ns_to_match = new ns(reinterpret_cast<const char *>(resolved_ns->prefix),
546                                  reinterpret_cast<const char *>(resolved_ns->href));
547         }
548 
549         xmlAttrPtr prop = xmlnode->properties;
550         for (; prop != NULL; prop = prop->next) {
551             if (xmlStrEqual(prop->name,
552                             reinterpret_cast<const xmlChar*>(name_to_match))) {
553                 if (ns_util::attr_ns_match(prop, ns_to_match)) {
554                     if (ns_to_match != nspace)
555                         delete ns_to_match;
556                     return prop;
557                 }
558             }
559         }
560 
561         if (ns_to_match != nspace)
562             delete ns_to_match;
563         return NULL;
564     }
565     //####################################################################
find_default_prop(xmlNodePtr xmlnode,const char * name,const ns * nspace)566     phantom_attr* find_default_prop (xmlNodePtr xmlnode,
567                                      const char *name, const ns *nspace) {
568         if (xmlnode->doc != 0) {
569             xmlAttributePtr dtd_attr = 0;
570             const xmlChar* prefix = 0;
571 
572             if (nspace && strlen(nspace->get_prefix()) > 0)
573                 prefix = reinterpret_cast<const xmlChar*>(nspace->get_prefix());
574 
575             if (xmlnode->doc->intSubset != 0) {
576                 if (nspace)
577                     dtd_attr = xmlGetDtdQAttrDesc(xmlnode->doc->intSubset,
578                                                   xmlnode->name,
579                                                   reinterpret_cast<const xmlChar*>(name),
580                                                   prefix);
581                 else
582                     dtd_attr = xmlGetDtdAttrDesc(xmlnode->doc->intSubset,
583                                                  xmlnode->name,
584                                                  reinterpret_cast<const xmlChar*>(name));
585             }
586 
587             if (dtd_attr == 0 && xmlnode->doc->extSubset != 0) {
588                 if (nspace)
589                     dtd_attr = xmlGetDtdQAttrDesc(xmlnode->doc->extSubset,
590                                                   xmlnode->name,
591                                                   reinterpret_cast<const xmlChar*>(name),
592                                                   prefix);
593                 else
594                     dtd_attr = xmlGetDtdAttrDesc(xmlnode->doc->extSubset,
595                                                  xmlnode->name,
596                                                  reinterpret_cast<const xmlChar*>(name));
597             }
598 
599             if (dtd_attr != 0 && dtd_attr->defaultValue != 0) {
600 
601                 node_private_data *  node_data = attach_node_private_data(xmlnode);
602 
603 
604                 // Found, now check the phantom attributes list attached to the
605                 // node
606                 phantom_attr *  current = node_data->phantom_attrs_;
607                 while (current != NULL) {
608                     if (current->def_prop_ == dtd_attr)
609                         return current;
610                     current = current->next;
611                 }
612 
613                 // Not found. Create a new phantom_attr structure
614                 phantom_attr *  new_phantom = new phantom_attr;
615                 memset( new_phantom, 0, sizeof( phantom_attr ) );
616                 new_phantom->def_prop_ = dtd_attr;
617 
618                 current = node_data->phantom_attrs_;
619                 new_phantom->next = current;
620                 node_data->phantom_attrs_ = new_phantom;
621                 return new_phantom;
622             }
623         }
624         return NULL;
625     }
626 }
627 
628     //####################################################################
operator ==(const attributes::iterator & lhs,const attributes::iterator & rhs)629     bool operator== (const attributes::iterator &lhs,
630                      const attributes::iterator &rhs) {
631         return *(lhs.pimpl_) == *(rhs.pimpl_);
632     }
633     //####################################################################
operator !=(const attributes::iterator & lhs,const attributes::iterator & rhs)634     bool operator!= (const attributes::iterator &lhs,
635                      const attributes::iterator &rhs) {
636         return !(lhs == rhs);
637     }
638     //####################################################################
operator ==(const attributes::const_iterator & lhs,const attributes::const_iterator & rhs)639     bool operator== (const attributes::const_iterator &lhs,
640                      const attributes::const_iterator &rhs) {
641         return *(lhs.pimpl_) == *(rhs.pimpl_);
642     }
643     //####################################################################
operator !=(const attributes::const_iterator & lhs,const attributes::const_iterator & rhs)644     bool operator!= (const attributes::const_iterator &lhs,
645                      const attributes::const_iterator &rhs) {
646         return !(lhs == rhs);
647     }
648     //####################################################################
649 namespace impl {
operator ==(const ait_impl & lhs,const ait_impl & rhs)650     bool operator== (const ait_impl &lhs, const ait_impl &rhs) {
651         return ((lhs.attr_.xmlnode_ == rhs.attr_.xmlnode_) &&
652                 (lhs.attr_.normalize() == rhs.attr_.normalize()));
653     }
654     //####################################################################
operator !=(const ait_impl & lhs,const ait_impl & rhs)655     bool operator!= (const ait_impl &lhs, const ait_impl &rhs) {
656         return !(lhs == rhs);
657     }
658 }
659     //####################################################################
660 }
661 //####################################################################
662