NCBI C++ ToolKit
attributes.cpp
Go to the documentation of this file.

Go to the SVN repository for this file.

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: attributes.cpp 79080 2017-08-09 18:22:55Z 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 contains the implementation of the xml::attributes class.
43 **/
44 
45 // xmlwrapp includes
48 #include "ait_impl.hpp"
49 #include "pimpl_base.hpp"
50 
51 // standard includes
52 #include <string.h>
53 #include <new>
54 #include <algorithm>
55 #include <list>
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 const char * kInsertError = "inserting attribute error";
65 
66 
67 //####################################################################
68 struct xml::attributes::pimpl : public pimpl_base<xml::attributes::pimpl> {
69  //####################################################################
70  pimpl (void) : owner_(true) {
71  xmlnode_ = xmlNewNode(0, reinterpret_cast<const xmlChar*>("blank"));
72  if (!xmlnode_) throw std::bad_alloc();
73  }
74  //####################################################################
75  pimpl (xmlNodePtr node) : xmlnode_(node), owner_(false)
76  {}
77  //####################################################################
78  pimpl (const pimpl &other) : owner_(true) {
79  xmlnode_ = xmlCopyNode(other.xmlnode_, 2);
80  if (!xmlnode_) throw std::bad_alloc();
81  }
82  //####################################################################
83  ~pimpl (void)
84  { release(); }
85  //####################################################################
86  void release (void)
87  { if (owner_ && xmlnode_) xmlFreeNode(xmlnode_); }
88  //####################################################################
89 
90  xmlNodePtr xmlnode_;
91  bool owner_;
92 
93  private:
94  pimpl & operator=(const pimpl &);
95 };
96 //####################################################################
98  pimpl_ = new pimpl;
99 }
100 //####################################################################
102  pimpl_ = new pimpl(0);
103 }
104 //####################################################################
106  pimpl_ = new pimpl(*other.pimpl_);
107 }
108 //####################################################################
110  attributes tmp(other);
111  swap(tmp);
112  return *this;
113 }
114 //####################################################################
116  std::swap(pimpl_, other.pimpl_);
117 }
118 //####################################################################
120  if (pimpl_ != NULL)
121  delete pimpl_;
122 }
123 //####################################################################
125  pimpl_(other.pimpl_)
126 {
127  other.pimpl_ = NULL;
128 }
129 //####################################################################
131  if (this != &other) {
132  if (pimpl_ != NULL)
133  delete pimpl_;
134  pimpl_ = other.pimpl_;
135  other.pimpl_ = NULL;
136  }
137  return *this;
138 }
139 //####################################################################
141  return pimpl_->xmlnode_;
142 }
143 //####################################################################
144 xml::ns xml::attributes::createUnsafeNamespace (void * libxml2RawNamespace) {
145  return xml::ns(libxml2RawNamespace);
146 }
147 //####################################################################
149  return name_space.unsafe_ns_;
150 }
151 //####################################################################
153  xmlNodePtr x = static_cast<xmlNodePtr>(node);
154 
155  pimpl_->release();
156  pimpl_->owner_ = false;
157  pimpl_->xmlnode_ = x;
158 }
159 //####################################################################
161  return iterator(pimpl_->xmlnode_,
162  pimpl_->xmlnode_->properties,
163  false, // not default
164  false); // not from find
165 }
166 //####################################################################
168  return const_iterator(pimpl_->xmlnode_,
169  pimpl_->xmlnode_->properties,
170  false, // not default
171  false); // not from find
172 }
173 //####################################################################
175  return iterator(pimpl_->xmlnode_,
176  NULL,
177  false,
178  false);
179 }
180 //####################################################################
182  return const_iterator(pimpl_->xmlnode_,
183  NULL,
184  false,
185  false);
186 }
187 //####################################################################
188 void xml::attributes::insert (const char *name, const char *value,
189  const ns *nspace ) {
190  if ( (!name) || (!value))
191  throw xml::exception("name and value of an attribute to "
192  "insert must not be NULL");
193  if (name[0] == '\0')
194  throw xml::exception("name cannot be empty");
195 
196  bool only_whitespaces = true;
197  const char * current = name;
198  while (*current != '\0') {
199  if (memchr(" \t\n\r", *current, 4) == NULL) {
200  only_whitespaces = false;
201  break;
202  }
203  ++current;
204  }
205  if (only_whitespaces)
206  throw xml::exception("name may not consist "
207  "of only whitespace characters");
208 
209 
210  const char * column = strchr(name, ':');
211 
212  if (!nspace) {
213  // It means ithat the user does not care about namespaces.
214  // So we should bother about them.
215  if (column) {
216  // The given name is qualified. Search for the namespace basing on
217  // the prefix
218  if (*(column + 1) == '\0')
219  throw xml::exception("invalid attribute name");
220  if (column == name)
221  throw xml::exception("an attribute may not have a default namespace");
222 
223  std::string prefix(name, column - name);
224  xmlNsPtr resolved_ns = xmlSearchNs(pimpl_->xmlnode_->doc,
225  pimpl_->xmlnode_,
226  reinterpret_cast<const xmlChar*>(prefix.c_str()));
227  if (!resolved_ns)
228  throw xml::exception("cannot resolve namespace");
229 
230  if (xmlSetNsProp(pimpl_->xmlnode_,
231  resolved_ns,
232  reinterpret_cast<const xmlChar*>(column + 1),
233  reinterpret_cast<const xmlChar*>(value)) == NULL)
235  return;
236  }
237 
238  // The given name is not qualified.
239  if (xmlSetProp(pimpl_->xmlnode_,
240  reinterpret_cast<const xmlChar*>(name),
241  reinterpret_cast<const xmlChar*>(value)) == NULL)
243  return;
244  }
245 
246  // Some namespace is provided. Check that the name is not qualified.
247  if (column)
248  throw xml::exception("cannot specify both a qualified name and a namespace");
249 
250  if (nspace->is_void()) {
251  // The user wanted to insert an attribute without a namespace at all.
252  if (xmlSetProp(pimpl_->xmlnode_,
253  reinterpret_cast<const xmlChar*>(name),
254  reinterpret_cast<const xmlChar*>(value)) == NULL)
256  return;
257  }
258 
259  if (nspace->get_prefix() == std::string(""))
260  throw xml::exception("an attribute may not have a default namespace");
261 
262  if (!nspace->is_safe()) {
263  if (xmlSetNsProp(pimpl_->xmlnode_,
264  reinterpret_cast<xmlNsPtr>(nspace->unsafe_ns_),
265  reinterpret_cast<const xmlChar*>(name),
266  reinterpret_cast<const xmlChar*>(value)) == NULL)
268  return;
269  }
270 
271 
272  // Resolve namespace basing on uri
273  xmlNsPtr resolved_ns = xmlSearchNsByHref(pimpl_->xmlnode_->doc,
274  pimpl_->xmlnode_,
275  reinterpret_cast<const xmlChar*>(nspace->get_uri()));
276  if (!resolved_ns)
277  throw xml::exception("inserting attribute error: "
278  "cannot resolve namespace");
279 
280  if (xmlSetNsProp(pimpl_->xmlnode_,
281  resolved_ns,
282  reinterpret_cast<const xmlChar*>(name),
283  reinterpret_cast<const xmlChar*>(value)) == NULL)
285  return;
286 }
287 //####################################################################
289  const ns *nspace) {
290  xmlAttrPtr prop = find_prop(pimpl_->xmlnode_, name, nspace);
291  if (prop != 0)
292  return iterator(pimpl_->xmlnode_, prop, false, true);
293 
294  phantom_attr* dtd_prop = find_default_prop(pimpl_->xmlnode_, name, nspace);
295  if (dtd_prop != 0)
296  return iterator(pimpl_->xmlnode_, dtd_prop, true, true);
297 
298  return iterator(pimpl_->xmlnode_, NULL, false, true);
299 }
300 //####################################################################
302  const ns *nspace) const {
303  xmlAttrPtr prop = find_prop(pimpl_->xmlnode_, name, nspace);
304  if (prop != 0)
305  return const_iterator(pimpl_->xmlnode_, prop, false, true);
306 
307  phantom_attr * dtd_prop = find_default_prop(pimpl_->xmlnode_, name, nspace);
308  if (dtd_prop != 0)
309  return const_iterator(pimpl_->xmlnode_, dtd_prop, true, true);
310 
311  return const_iterator(pimpl_->xmlnode_, NULL, false, true);
312 }
313 //####################################################################
315  // Check that the iterator is initialized and
316  // that it is of the same node
317  if (to_erase == iterator() ||
318  to_erase.pimpl_->get()->xmlnode_ != pimpl_->xmlnode_)
319  throw xml::exception("cannot erase attribute, the iterator is "
320  "not initialized or belongs to another node attributes");
321 
322  // There is nothing to erase for these two cases
323  if (to_erase == end()) // It must be first because otherwise end() is referenced
324  return end();
325  if (to_erase->is_default())
326  return end();
327 
328  // It's not default so we should delete it
329  xmlAttrPtr prop_to_erase = static_cast<xmlAttrPtr>(to_erase.pimpl_->get()->prop_);
330  to_erase.pimpl_->get()->prop_ = static_cast<xmlAttrPtr>(to_erase.pimpl_->get()->prop_)->next;
331 
332  xmlUnsetNsProp(pimpl_->xmlnode_, prop_to_erase->ns, prop_to_erase->name);
333  return to_erase;
334 }
335 //####################################################################
336 xml::attributes::size_type xml::attributes::erase (const char *name, const ns *nspace) {
337 
338  if (!name)
339  return 0;
340 
341  const char * column = strchr(name, ':');
342  if (!nspace) {
343  if (column) {
344  // name is qualified
345  if (column == name)
346  return 0; // Default NS is provided
347  if (*(column + 1) == '\0')
348  return 0; // No name is provided
349 
350  std::string prefix(name, column - name);
351  xmlNsPtr resolved_ns = xmlSearchNs(pimpl_->xmlnode_->doc,
352  pimpl_->xmlnode_,
353  reinterpret_cast<const xmlChar*>(prefix.c_str()));
354  if (!resolved_ns)
355  return 0;
356 
357  if (xmlUnsetNsProp(pimpl_->xmlnode_,
358  resolved_ns,
359  reinterpret_cast<const xmlChar*>(column + 1)) == 0)
360  return 1;
361  return 0;
362  }
363 
364  // It is a non-qualified name. Search basing on names only.
365  size_type count = 0;
366  xmlAttrPtr att = find_prop(pimpl_->xmlnode_, name, NULL);
367  while (att != NULL) {
368  ++count;
369  xmlUnlinkNode(reinterpret_cast<xmlNodePtr>(att));
370  xmlFreeProp(att);
371  att = find_prop(pimpl_->xmlnode_, name, NULL);
372  }
373  return count;
374  }
375 
376  if (column)
377  return 0; // Both a namespace and a prefix in the name are provided
378 
379  if (nspace->is_void()) {
380  // The attribute must not have a namespace at all
381  if (xmlUnsetProp(pimpl_->xmlnode_,
382  reinterpret_cast<const xmlChar*>(name)) == 0)
383  return 1;
384  return 0;
385  }
386 
387  if (!nspace->is_safe()) {
388  if (xmlUnsetNsProp(pimpl_->xmlnode_,
389  reinterpret_cast<xmlNsPtr>(nspace->unsafe_ns_),
390  reinterpret_cast<const xmlChar*>(name)) == 0)
391  return 1;
392  return 0;
393  }
394 
395  // Safe namespace. Resolve it basing on the uri.
396  xmlNsPtr resolved_ns = xmlSearchNsByHref(pimpl_->xmlnode_->doc,
397  pimpl_->xmlnode_,
398  reinterpret_cast<const xmlChar*>(nspace->get_uri()));
399 
400  if (!resolved_ns)
401  return 0; // Namespace is not resolved. Nothing will be deleted anyway.
402 
403  // There could be many attributes with different prefixes but with the same
404  // URIs and names
405  size_type count = 0;
406  while (xmlUnsetNsProp(pimpl_->xmlnode_,
407  resolved_ns,
408  reinterpret_cast<const xmlChar*>(name)) == 0)
409  ++count;
410  return count;
411 }
412 //####################################################################
413 bool xml::attributes::empty (void) const {
414  return pimpl_->xmlnode_->properties == 0;
415 }
416 //####################################################################
418  size_type count = 0;
419 
420  xmlAttrPtr prop = pimpl_->xmlnode_->properties;
421  while (prop != 0) { ++count; prop = prop->next; }
422 
423  return count;
424 }
425 //####################################################################
426 struct attr_cmp {
427  bool operator() (const xmlAttrPtr & lhs, const xmlAttrPtr & rhs) const {
428  return strcmp(reinterpret_cast<const char *>(lhs->name),
429  reinterpret_cast<const char *>(rhs->name)) < 0;
430  }
431 };
433  std::list<xmlAttrPtr> attrs;
434  xmlAttrPtr prop = pimpl_->xmlnode_->properties;
435 
436  // Collect pointers to the attributes
437  while (prop != 0) {
438  attrs.push_back(prop);
439  prop = prop->next;
440  }
441 
442  // Sort pointers by the attribute names
443  attrs.sort(attr_cmp());
444 
445  // Modify the list of attributes to have them sorted
446  xmlAttrPtr cur = NULL;
447  xmlAttrPtr prev = NULL;
448 
449  for (std::list<xmlAttrPtr>::const_iterator k = attrs.begin();
450  k != attrs.end(); ++k) {
451  cur = *k;
452 
453  if (prev == NULL)
454  pimpl_->xmlnode_->properties = cur;
455 
456  cur->prev = prev;
457  cur->next = NULL;
458 
459  if (prev != NULL)
460  prev->next = cur;
461 
462  prev = cur;
463  }
464 }
465 //####################################################################
466 
This file defines the xml::ait_impl class.
const char * kInsertError
Definition: attributes.cpp:64
This file contains the definition of the xml::attributes class.
bool is_default(void) const
Test if the attribute is default.
Definition: ait_impl.cpp:329
Const Iterator class for accessing attribute pairs.
Definition: attributes.hpp:320
Iterator class for accessing attribute pairs.
Definition: attributes.hpp:285
impl::ait_impl * pimpl_
Definition: attributes.hpp:310
The xml::attributes class is used to access all the attributes of one xml::node.
Definition: attributes.hpp:78
std::size_t size_type
size type
Definition: attributes.hpp:80
size_type size(void) const
Find out how many attributes there are in this xml::attributes object.
Definition: attributes.cpp:417
iterator erase(iterator to_erase)
Erase the attribute that is pointed to by the given iterator.
Definition: attributes.cpp:314
void swap(attributes &other)
Swap this xml::attributes object with another one.
Definition: attributes.cpp:115
attributes(void)
Create a new xml::attributes object with no attributes.
Definition: attributes.cpp:97
iterator find(const char *name, const ns *nspace=NULL)
Find the attribute with the given name and namespace.
Definition: attributes.cpp:288
iterator begin(void)
Get an iterator that points to the first attribute.
Definition: attributes.cpp:160
static xml::ns createUnsafeNamespace(void *libxml2RawNamespace)
Definition: attributes.cpp:144
static void * getUnsafeNamespacePointer(const xml::ns &name_space)
Definition: attributes.cpp:148
void sort(void)
Sorts the attributes in place.
Definition: attributes.cpp:432
attributes & operator=(const attributes &other)
Copy the given xml::attributes object into this one.
Definition: attributes.cpp:109
iterator end(void)
Get an iterator that points one past the the last attribute.
Definition: attributes.cpp:174
void set_data(void *node)
Definition: attributes.cpp:152
bool empty(void) const
Find out if there are any attributes in this xml::attributes object.
Definition: attributes.cpp:413
virtual ~attributes(void)
Clean up after a xml::attributes object.
Definition: attributes.cpp:119
void insert(const char *name, const char *value, const ns *nspace=NULL)
Add an attribute to the attributes list.
Definition: attributes.cpp:188
void * get_data(void)
Definition: attributes.cpp:140
This exception class is thrown by xmlwrapp for all runtime XML-related errors along with the xml::par...
Definition: exception.hpp:64
attributes::attr * get(void)
Definition: ait_impl.cpp:100
The xml::node class is used to hold information about one XML node.
Definition: node.hpp:106
The xml::ns class is used to access and handle namespaces of nodes and attributes.
Definition: namespace.hpp:64
void * unsafe_ns_
Definition: namespace.hpp:196
const char * get_prefix(void) const
Get the namespace prefix.
Definition: namespace.cpp:95
const char * get_uri(void) const
Get the namespace URI.
Definition: namespace.cpp:109
bool is_safe(void) const
Check if the object is safe i.e.
Definition: namespace.cpp:154
bool is_void(void) const
If a node or an attribute has no namespace, then a namespace with empty prefix and empty URI is retur...
Definition: namespace.cpp:123
#define true
Definition: bool.h:35
#define false
Definition: bool.h:36
static DLIST_TYPE *DLIST_NAME() prev(DLIST_LIST_TYPE *list, DLIST_TYPE *item)
Definition: dlist.tmpl.h:61
static DLIST_TYPE *DLIST_NAME() next(DLIST_LIST_TYPE *list, DLIST_TYPE *item)
Definition: dlist.tmpl.h:56
static const char * column
Definition: stats.c:23
static char tmp[3200]
Definition: utf8.c:42
void swap(NCBI_NS_NCBI::pair_base_member< T1, T2 > &pair1, NCBI_NS_NCBI::pair_base_member< T1, T2 > &pair2)
Definition: ncbimisc.hpp:1508
string
Definition: cgiapp.hpp:687
#define NULL
Definition: ncbistd.hpp:225
This file contains the definition of the xml::exception class.
const GenericPointer< typename T::ValueType > T2 value
Definition: pointer.h:1227
int strcmp(const char *str1, const char *str2)
Definition: odbc_utils.hpp:160
phantom_attr * find_default_prop(xmlNodePtr xmlnode, const char *name, const ns *nspace)
Definition: ait_impl.cpp:567
xmlAttrPtr find_prop(xmlNodePtr xmlnode, const char *name, const ns *nspace)
Definition: ait_impl.cpp:512
XML library namespace.
Definition: attributes.hpp:57
static const char * prefix[]
Definition: pcregrep.c:405
pimpl(const pimpl &other)
Definition: attributes.cpp:78
pimpl & operator=(const pimpl &)
pimpl(xmlNodePtr node)
Definition: attributes.cpp:75
Modified on Mon Jun 17 05:06:07 2024 by modify_doxy.py rev. 669887