Coverage Report

Created: 2026-10-28 07:09

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/exiv2/xmpsdk/src/XML_Node.cpp
Line
Count
Source
1
// =================================================================================================
2
// Copyright 2005-2007 Adobe Systems Incorporated
3
// All Rights Reserved.
4
//
5
// NOTICE:  Adobe permits you to use, modify, and distribute this file in accordance with the terms
6
// of the Adobe license agreement accompanying it.
7
// =================================================================================================
8
9
#include "XMP_Environment.h"  // ! Must be the first #include!
10
#include "XMLParserAdapter.hpp"
11
12
#include <map>
13
#include <cstring>
14
#include <cstdio>
15
16
// ! Can't include XMP..._Impl.hpp - used by both Core and Files.
17
0
#define XMP_LitNMatch(s,l,n)  (std::strncmp((s),(l),(n)) == 0)
18
19
#if XMP_WinBuild
20
#ifdef _MSC_VER
21
  #define snprintf _snprintf
22
  #pragma warning ( disable : 4996 )  // snprintf is safe
23
#endif
24
#endif
25
26
// =================================================================================================
27
28
#if 0 // Pattern for iterating over the children or attributes:
29
  for ( size_t xxNum = 0, xxLim = _node_->_offspring_->size(); xxNum < xxLim; ++xxNum ) {
30
    const XML_NodePtr _curr_ = _node_->_offspring_[xxNum];
31
  }
32
#endif
33
34
// =================================================================================================
35
// XML_Node::IsWhitespaceNode
36
//===========================
37
38
bool XML_Node::IsWhitespaceNode() const
39
252k
{
40
252k
  if ( this->kind != kCDataNode ) return false;
41
42
657k
  for ( size_t i = 0; i < this->value.size(); ++i ) {
43
471k
    unsigned char ch = this->value[i];
44
471k
    if ( IsWhitespaceChar ( ch ) ) continue;
45
    // *** Add checks for other whitespace characters.
46
358
    return false; // All the checks failed, this isn't whitespace.
47
471k
  }
48
49
186k
  return true;
50
51
187k
}  // XML_Node::IsWhitespaceNode
52
53
// =================================================================================================
54
// XML_Node::IsLeafContentNode
55
//============================
56
57
bool XML_Node::IsLeafContentNode() const
58
0
{
59
0
  if ( this->kind != kElemNode ) return false;
60
0
  if ( this->content.size() == 0 ) return true;
61
0
  if ( this->content.size() > 1 ) return false;
62
0
  if ( this->content[0]->kind != kCDataNode ) return false;
63
64
0
  return true;
65
66
0
}  // XML_Node::IsLeafContentNode
67
68
// =================================================================================================
69
// XML_Node::IsEmptyLeafNode
70
//==========================
71
72
bool XML_Node::IsEmptyLeafNode() const
73
0
{
74
75
0
  if ( (this->kind != kElemNode) || (this->content.size() != 0) ) return false;
76
0
  return true;
77
78
0
}  // XML_Node::IsEmptyLeafNode
79
80
// =================================================================================================
81
// XML_Node::GetAttrValue
82
//=======================
83
84
XMP_StringPtr XML_Node::GetAttrValue ( XMP_StringPtr attrName ) const
85
0
{
86
87
0
  for ( size_t i = 0, aLim = this->attrs.size(); i < aLim; ++i ) {
88
0
    XML_Node * attrPtr = this->attrs[i];
89
0
    if ( ! attrPtr->ns.empty() ) continue; // This form of GetAttrValue is for attrs in no namespace.
90
0
    if ( attrPtr->name == attrName ) return attrPtr->value.c_str();
91
0
  }
92
  
93
0
  return 0; // Not found.
94
95
0
}  // XML_Node::GetAttrValue
96
97
// =================================================================================================
98
// XML_Node::SetAttrValue
99
//=======================
100
101
void XML_Node::SetAttrValue ( XMP_StringPtr attrName, XMP_StringPtr attrValue )
102
0
{
103
  
104
0
  for ( size_t i = 0, aLim = this->attrs.size(); i < aLim; ++i ) {
105
0
    XML_Node * attrPtr = this->attrs[i];
106
0
    if ( ! attrPtr->ns.empty() ) continue; // This form of SetAttrValue is for attrs in no namespace.
107
0
    if ( attrPtr->name == attrName ) {
108
0
      attrPtr->value = attrValue;
109
0
      return;
110
0
    }
111
0
  }
112
113
0
}  // XML_Node::SetAttrValue
114
115
// =================================================================================================
116
// XML_Node::GetLeafContentValue
117
//==============================
118
119
XMP_StringPtr XML_Node::GetLeafContentValue() const
120
0
{
121
0
  if ( (! this->IsLeafContentNode()) || this->content.empty() ) return "";
122
123
0
  return this->content[0]->value.c_str();
124
125
0
}  // XML_Node::GetLeafContentValue
126
127
// =================================================================================================
128
// XML_Node::SetLeafContentValue
129
//==============================
130
131
void XML_Node::SetLeafContentValue ( XMP_StringPtr newValue )
132
0
{
133
0
  XML_Node * valueNode;
134
  
135
0
  if ( ! this->content.empty() ) {
136
0
    valueNode = this->content[0];
137
0
  } else {
138
0
    valueNode = new XML_Node ( this, "", kCDataNode );
139
0
    this->content.push_back ( valueNode );
140
0
  }
141
142
0
  valueNode->value = newValue;
143
144
0
}  // XML_Node::SetLeafContentValue
145
146
// =================================================================================================
147
// XML_Node::CountNamedElements
148
//=============================
149
150
size_t XML_Node::CountNamedElements ( XMP_StringPtr nsURI, XMP_StringPtr localName ) const
151
0
{
152
0
  size_t count = 0;
153
  
154
0
  for ( size_t i = 0, vLim = this->content.size(); i < vLim; ++i ) {
155
0
    const XML_Node & child = *this->content[i];
156
0
    if ( child.ns != nsURI ) continue;
157
0
    if ( strcmp ( localName, child.name.c_str()+child.nsPrefixLen ) != 0 ) continue;
158
0
    ++count;
159
0
  }
160
  
161
0
  return count;
162
  
163
0
}  // XML_Node::CountNamedElements
164
165
// =================================================================================================
166
// XML_Node::GetNamedElement
167
//==========================
168
169
XML_NodePtr XML_Node::GetNamedElement ( XMP_StringPtr nsURI, XMP_StringPtr localName, size_t which /* = 0 */ )
170
0
{
171
172
0
  for ( size_t i = 0, vLim = this->content.size(); i < vLim; ++i ) {
173
0
    XML_Node * childPtr = this->content[i];
174
0
    if ( childPtr->ns != nsURI ) continue;
175
0
    if ( strcmp ( localName, childPtr->name.c_str()+childPtr->nsPrefixLen ) != 0 ) continue;
176
0
    if ( which == 0 ) return childPtr;
177
0
    --which;
178
0
  }
179
  
180
0
  return 0; /// Not found.
181
182
0
}  // XML_Node::GetNamedElement
183
184
// =================================================================================================
185
// DumpNodeList
186
// ============
187
188
static const char * kNodeKinds[] = { "root", "elem", "attr", "cdata", "pi" };
189
190
static void DumpNodeList ( std::string * buffer, const XML_NodeVector & list, int indent )
191
0
{
192
193
0
  for ( size_t i = 0, limit = list.size(); i < limit; ++i ) {
194
195
0
    const XML_Node * node = list[i];
196
    
197
0
    for ( int t = indent; t > 0; --t ) *buffer += "  ";
198
0
    if ( node->IsWhitespaceNode() ) {
199
0
      *buffer += "-- whitespace --\n";
200
0
      continue;
201
0
    }
202
203
0
    *buffer += node->name;
204
0
    *buffer += " - ";
205
0
    *buffer += kNodeKinds[node->kind];
206
0
    if ( ! node->value.empty() ) {
207
0
      *buffer += ", value=\"";
208
0
      *buffer += node->value;
209
0
      *buffer += "\"";
210
0
    }
211
0
    if ( ! node->ns.empty() ) {
212
0
      *buffer += ", ns=\"";
213
0
      *buffer += node->ns;
214
0
      *buffer += "\"";
215
0
    }
216
0
    if ( node->nsPrefixLen != 0 ) {
217
0
      *buffer += ", prefixLen=";
218
0
      char numBuf [20];
219
0
      snprintf ( numBuf, sizeof(numBuf), "%lu", (unsigned long)node->nsPrefixLen );
220
0
      *buffer += numBuf;
221
0
    }
222
0
    *buffer += "\n";
223
  
224
0
    if ( ! node->attrs.empty() ) {
225
0
      for ( int t = indent+1; t > 0; --t ) *buffer += "  ";
226
0
      *buffer += "attrs:\n";
227
0
      DumpNodeList ( buffer, node->attrs, indent+2 );
228
0
    }
229
    
230
0
    if ( ! node->content.empty() ) {
231
0
      DumpNodeList ( buffer, node->content, indent+1 );
232
0
    }
233
234
0
  }
235
236
0
}  // DumpNodeList
237
238
// =================================================================================================
239
// XML_Node::Dump
240
//===============
241
242
void XML_Node::Dump ( std::string * buffer )
243
0
{
244
245
0
  *buffer = "Dump of XML_Node tree\n";
246
247
0
  *buffer += "Root info: name=\"";
248
0
  *buffer += this->name;
249
0
  *buffer += "\", value=\"";
250
0
  *buffer += this->value;
251
0
  *buffer += "\", ns=\"";
252
0
  *buffer += this->ns;
253
0
  *buffer += "\", kind=";
254
0
  *buffer += kNodeKinds[this->kind];
255
0
  *buffer += "\n";
256
  
257
0
  if ( ! this->attrs.empty() ) {
258
0
    *buffer += "  attrs:\n";
259
0
    DumpNodeList ( buffer, this->attrs, 2 );
260
0
  }
261
0
  *buffer += "\n";
262
  
263
0
  DumpNodeList ( buffer, this->content, 0 );
264
265
0
}  // XML_Node::Dump
266
267
// =================================================================================================
268
// SerializeOneNode
269
// ================
270
271
static void SerializeOneNode ( std::string * buffer, const XML_Node & node )
272
0
{
273
0
  size_t i, limit;
274
0
  XMP_StringPtr namePtr = node.name.c_str();
275
0
  if ( XMP_LitNMatch ( namePtr, "_dflt_:", 7 ) ) namePtr += 7; // Hack for default namespaces.
276
277
0
  switch ( node.kind ) {
278
  
279
0
    case kElemNode:
280
0
      *buffer += '<';
281
0
      *buffer += namePtr;
282
0
      for ( i = 0, limit = node.attrs.size(); i < limit; ++i ) {
283
0
        SerializeOneNode ( buffer, *node.attrs[i] );
284
0
      }
285
0
      if ( node.content.empty() ) {
286
0
        *buffer += "/>";
287
0
      } else {
288
0
        *buffer += '>';
289
0
        for ( i = 0, limit = node.content.size(); i < limit; ++i ) {
290
0
          SerializeOneNode ( buffer, *node.content[i] );
291
0
        }
292
0
        *buffer += "</";
293
0
        *buffer += namePtr;
294
0
        *buffer += '>';
295
0
      }
296
0
      break;
297
  
298
0
    case kAttrNode:
299
0
      *buffer += ' ';
300
0
      *buffer += namePtr;
301
0
      *buffer += "=\"";
302
0
      *buffer += node.value;
303
0
      *buffer += '"';
304
0
      break;
305
  
306
0
    case kCDataNode:
307
0
      *buffer += node.value;
308
0
      break;
309
  
310
0
    case kPINode:
311
0
      *buffer += node.value;  // *** Note that we're dropping PIs during the Expat parse.
312
0
      break;
313
  
314
0
  }
315
316
0
}  // SerializeOneNode
317
318
// =================================================================================================
319
// CollectNamespaceDecls
320
// =====================
321
322
typedef std::map < std::string, std::string > NamespaceMap;
323
324
static void CollectNamespaceDecls ( NamespaceMap * nsMap, const XML_Node & node )
325
0
{
326
0
  size_t i, limit;
327
328
0
  if ( ! node.ns.empty() ) {
329
0
    size_t nameMid = 0;
330
0
    while ( node.name[nameMid] != ':' ) ++nameMid;
331
0
    std::string prefix = node.name.substr ( 0, nameMid );
332
0
    (*nsMap)[prefix] = node.ns;
333
0
  }
334
  
335
0
  if ( node.kind == kElemNode ) {
336
337
0
    for ( i = 0, limit = node.attrs.size(); i < limit; ++i ) {
338
0
      CollectNamespaceDecls ( nsMap, *node.attrs[i] );
339
0
    }
340
341
0
    for ( i = 0, limit = node.content.size(); i < limit; ++i ) {
342
0
      const XML_Node & content = *node.content[i];
343
0
      if ( content.kind == kElemNode ) CollectNamespaceDecls ( nsMap, content );
344
0
    }
345
346
0
  }
347
  
348
0
}  // CollectNamespaceDecls
349
350
// =================================================================================================
351
// XML_Node::Serialize
352
//====================
353
354
void XML_Node::Serialize ( std::string * buffer )
355
0
{
356
0
  buffer->erase();
357
  
358
0
  if ( this->kind != kRootNode ) {
359
360
0
    SerializeOneNode ( buffer, *this );
361
362
0
  } else {
363
  
364
    // Do the outermost level here, in order to add the XML version and namespace declarations.
365
366
0
    *buffer += "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
367
368
0
    for ( size_t outer = 0, oLimit = this->content.size(); outer < oLimit; ++outer ) {
369
370
0
      const XML_Node & node = *this->content[outer];
371
372
0
      if ( node.kind != kElemNode ) {
373
374
0
        SerializeOneNode ( buffer, node );
375
376
0
      } else {
377
378
0
        XMP_StringPtr namePtr = node.name.c_str();
379
0
        if ( XMP_LitNMatch ( namePtr, "_dflt_:", 7 ) ) namePtr += 7; // Hack for default namespaces.
380
381
0
        *buffer += '<';
382
0
        *buffer += namePtr;
383
        
384
0
        NamespaceMap nsMap;
385
0
        CollectNamespaceDecls ( &nsMap, node );
386
0
        NamespaceMap::iterator nsDecl = nsMap.begin();
387
0
        NamespaceMap::iterator nsEnd  = nsMap.end();
388
0
        for ( ; nsDecl != nsEnd; ++nsDecl ) {
389
0
          const std::string & prefix = nsDecl->first;
390
0
          *buffer += " xmlns";
391
0
          if ( prefix != "_dflt_" ) { *buffer += ':'; *buffer += prefix; }
392
0
          *buffer += "=\"";
393
0
          *buffer += nsDecl->second;
394
0
          *buffer += '"';
395
0
        }
396
397
0
        for ( size_t attr = 0, aLimit = node.attrs.size(); attr < aLimit; ++attr ) {
398
0
          SerializeOneNode ( buffer, *node.attrs[attr] );
399
0
        }
400
401
0
        if ( node.content.empty() ) {
402
0
          *buffer += "/>";
403
0
        } else {
404
0
          *buffer += '>';
405
0
          for ( size_t child = 0, cLimit = node.content.size(); child < cLimit; ++child ) {
406
0
            SerializeOneNode ( buffer, *node.content[child] );
407
0
          }
408
0
          *buffer += "</";
409
0
          *buffer += namePtr;
410
0
          *buffer += '>';
411
0
        }
412
413
0
      }
414
415
0
    }
416
    
417
0
  }
418
  
419
420
0
}  // XML_Node::Serialize
421
422
// =================================================================================================
423
// XML_Node::RemoveAttrs
424
//======================
425
426
void XML_Node::RemoveAttrs()
427
533k
{
428
429
652k
  for ( size_t i = 0, vLim = this->attrs.size(); i < vLim; ++i ) delete this->attrs[i];
430
533k
  this->attrs.clear();
431
432
533k
}  // XML_Node::RemoveAttrs
433
434
// =================================================================================================
435
// XML_Node::RemoveContent
436
//========================
437
438
void XML_Node::RemoveContent()
439
533k
{
440
441
947k
  for ( size_t i = 0, vLim = this->content.size(); i < vLim; ++i ) delete this->content[i];
442
533k
  this->content.clear();
443
444
533k
}  // XML_Node::RemoveContent
445
446
// =================================================================================================
447
// XML_Node::ClearNode
448
//====================
449
450
void XML_Node::ClearNode()
451
0
{
452
453
0
  this->kind = 0;
454
0
  this->ns.erase();
455
0
  this->name.erase();
456
0
  this->value.erase();
457
458
0
  this->RemoveAttrs();
459
0
  this->RemoveContent();
460
461
0
}  // XML_Node::ClearNode
462
463
// =================================================================================================