Coverage Report

Created: 2025-08-03 06:58

/src/exiv2/xmpsdk/src/XMPIterator.cpp
Line
Count
Source (jump to first uncovered line)
1
// =================================================================================================
2
// Copyright 2002-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"  // ! This must be the first include!
10
#include "XMPCore_Impl.hpp"
11
12
#include "XMPIterator.hpp"
13
14
#include <string>
15
#include <stdio.h>  // For snprintf.
16
17
#if XMP_WinBuild
18
    #ifdef _MSC_VER
19
        #pragma warning ( disable : 4702 )  // unreachable code
20
        #pragma warning ( disable : 4800 )  // forcing value to bool 'true' or 'false' (performance warning)
21
        #pragma warning ( disable : 4996 )  // '...' was declared deprecated
22
    #endif
23
#endif
24
25
// =================================================================================================
26
// Support Routines
27
// =================================================================================================
28
29
30
#ifndef TraceIterators
31
  #define TraceIterators 0
32
#endif
33
34
#if TraceIterators
35
  static const char * sStageNames[] = { "before", "self", "qualifiers", "children" };
36
#endif
37
38
static XMP_Node * sDummySchema = 0; // ! Used for some ugliness with aliases.
39
40
// -------------------------------------------------------------------------------------------------
41
// AddSchemaProps
42
// --------------
43
//
44
// Add the top level properties to the IterNode for a schema.
45
46
static void
47
AddSchemaProps ( IterInfo & info, IterNode & iterSchema, const XMP_Node * xmpSchema )
48
0
{
49
0
  UNUSED(info);
50
  #if TraceIterators
51
    printf ( "    Adding properties of %s\n", xmpSchema->name.c_str() );
52
  #endif
53
54
0
  for ( size_t propNum = 0, propLim = xmpSchema->children.size(); propNum != propLim; ++propNum ) {
55
0
    const XMP_Node * xmpProp = xmpSchema->children[propNum];
56
    // *** set the has-aliases bit when appropriate
57
0
    iterSchema.children.push_back ( IterNode ( xmpProp->options, xmpProp->name, 0 ) );
58
    #if TraceIterators
59
      printf ( "        %s\n", xmpProp->name.c_str() );
60
    #endif
61
0
  }
62
63
0
}  // AddSchemaProps
64
65
// -------------------------------------------------------------------------------------------------
66
// AddSchemaAliases
67
// ----------------
68
//
69
// Add the aliases to the IterNode for a schema, if the corresponding actual exists.
70
71
static void
72
AddSchemaAliases ( IterInfo & info, IterNode & iterSchema, XMP_StringPtr schemaURI )
73
0
{
74
  
75
  // We're showing the aliases also. Look them up by their namespace prefix. Yes, the alias map is
76
  // sorted so we could process just that portion. But that takes more code and the extra speed
77
  // isn't worth it. (Plus this way we avoid a dependence on the map implementation.) Lookup the
78
  // XMP node from the alias, to make sure the actual exists.
79
  
80
  #if TraceIterators
81
    printf ( "    Adding aliases\n", schemaURI );
82
  #endif
83
84
0
  XMP_StringPtr nsPrefix;
85
0
  XMP_StringLen nsLen;
86
0
  bool found = XMPMeta::GetNamespacePrefix ( schemaURI, &nsPrefix, &nsLen );
87
0
  if ( ! found ) XMP_Throw ( "Unknown iteration namespace", kXMPErr_BadSchema );
88
  
89
0
  XMP_AliasMapPos currAlias = sRegisteredAliasMap->begin();
90
0
  XMP_AliasMapPos endAlias  = sRegisteredAliasMap->end();
91
  
92
0
  for ( ; currAlias != endAlias; ++currAlias ) {
93
0
    if ( XMP_LitNMatch ( currAlias->first.c_str(), nsPrefix, nsLen ) ) {
94
0
      const XMP_Node * actualProp = FindConstNode ( &info.xmpObj->tree, currAlias->second );
95
0
      if ( actualProp != 0 ) {
96
0
        iterSchema.children.push_back ( IterNode ( (actualProp->options | kXMP_PropIsAlias), currAlias->first, 0 ) );
97
        #if TraceIterators
98
          printf ( "        %s  =>  %s\n", currAlias->first.c_str(), actualProp->name.c_str() );
99
        #endif
100
0
      }
101
0
    }
102
0
  }
103
104
0
}  // AddSchemaAliases
105
106
// -------------------------------------------------------------------------------------------------
107
// AddNodeOffspring
108
// ----------------
109
//
110
// Add the immediate children and qualifiers to an IterNode.
111
112
static void
113
AddNodeOffspring ( IterInfo & info, IterNode & iterParent, const XMP_Node * xmpParent )
114
0
{
115
0
  XMP_VarString currPath ( iterParent.fullPath );
116
0
  size_t        leafOffset = iterParent.fullPath.size();
117
  
118
0
  if ( (! xmpParent->qualifiers.empty()) && (! (info.options & kXMP_IterOmitQualifiers)) ) {
119
120
    #if TraceIterators
121
      printf ( "    Adding qualifiers of %s\n", currPath.c_str() );
122
    #endif
123
124
0
    currPath += "/?"; // All qualifiers are named and use paths like "Prop/?Qual".
125
0
    leafOffset += 2;
126
    
127
0
    for ( size_t qualNum = 0, qualLim = xmpParent->qualifiers.size(); qualNum != qualLim; ++qualNum ) {
128
0
      const XMP_Node * xmpQual = xmpParent->qualifiers[qualNum];
129
0
      currPath += xmpQual->name;
130
0
      iterParent.qualifiers.push_back ( IterNode ( xmpQual->options, currPath, leafOffset ) );
131
0
      currPath.erase ( leafOffset );
132
      #if TraceIterators
133
        printf ( "        %s\n", xmpQual->name.c_str() );
134
      #endif
135
0
    }
136
    
137
0
    leafOffset -= 2;
138
0
    currPath.erase ( leafOffset );
139
140
0
  }
141
142
0
  if ( ! xmpParent->children.empty() ) {
143
  
144
    #if TraceIterators
145
      printf ( "    Adding children of %s\n", currPath.c_str() );
146
    #endif
147
148
0
    XMP_Assert ( xmpParent->options & kXMP_PropCompositeMask );
149
    
150
0
    if ( xmpParent->options & kXMP_PropValueIsStruct ) {
151
0
      currPath += '/';
152
0
      leafOffset += 1;
153
0
    }
154
    
155
0
    for ( size_t childNum = 0, childLim = xmpParent->children.size(); childNum != childLim; ++childNum ) {
156
0
      const XMP_Node * xmpChild = xmpParent->children[childNum];
157
0
      if ( ! (xmpParent->options & kXMP_PropValueIsArray) ) {
158
0
        currPath += xmpChild->name;
159
0
      } else {
160
0
        char buffer [32]; // AUDIT: Using sizeof(buffer) below for snprintf length is safe.
161
0
        snprintf ( buffer, sizeof(buffer), "[%lu]", static_cast<unsigned long>(childNum+1) ); // ! XPath indices are one-based.
162
0
        currPath += buffer;
163
0
      }
164
0
      iterParent.children.push_back ( IterNode ( xmpChild->options, currPath, leafOffset ) );
165
0
      currPath.erase ( leafOffset );
166
      #if TraceIterators
167
        printf ( "        %s\n", (iterParent.children.back().fullPath.c_str() + leafOffset) );
168
      #endif
169
0
    }
170
  
171
0
  }
172
173
0
}  // AddNodeOffspring
174
175
// -------------------------------------------------------------------------------------------------
176
// SetCurrSchema
177
// -------------
178
179
static inline void
180
SetCurrSchema ( IterInfo & info, XMP_StringPtr schemaName )
181
0
{
182
183
0
  info.currSchema = schemaName;
184
  #if 0 // *** XMP_DebugBuild
185
    info._schemaPtr = info.currSchema.c_str();
186
  #endif
187
188
0
}  // SetCurrSchema
189
190
static inline void
191
SetCurrSchema ( IterInfo & info, XMP_VarString & schemaName )
192
0
{
193
194
0
  info.currSchema = schemaName;
195
  #if 0 // *** XMP_DebugBuild
196
    info._schemaPtr = info.currSchema.c_str();
197
  #endif
198
199
0
}  // SetCurrSchema
200
201
// -------------------------------------------------------------------------------------------------
202
// AdvanceIterPos
203
// --------------
204
//
205
// Adjust currPos and possibly endPos for the next step in a pre-order depth-first traversal. The
206
// current node has just been visited, move on to its qualifiers, children, then siblings, or back
207
// up to an ancestor. AdvanceIterPos either moves to a property or qualifier node that can be
208
// visited, or to the end of the entire iteration.
209
210
static void
211
AdvanceIterPos ( IterInfo & info )
212
0
{
213
  // -------------------------------------------------------------------------------------------
214
  // Keep looking until we find a node to visit or the end of everything. The first time through
215
  // the current node will exist, we just visited it. But we have to keep looking if the current
216
  // node was the last of its siblings or is an empty schema.
217
  
218
  // ! It is possible that info.currPos == info.endPos on entry. Don't dereference info.currPos yet!
219
220
0
  while ( true ) {
221
  
222
0
    if ( info.currPos == info.endPos ) {
223
    
224
      // ------------------------------------------------------------------------------------
225
      // At the end of a set of siblings, move up to an ancestor. We've either just finished
226
      // the qualifiers and will move to the children, or have just finished the children and
227
      // will move on to the next sibling.
228
      
229
0
      if ( info.ancestors.empty() ) break; // We're at the end of the schema list.
230
231
0
      IterPosPair & parent = info.ancestors.back();
232
0
      info.currPos = parent.first;
233
0
      info.endPos  = parent.second;
234
0
      info.ancestors.pop_back();
235
      
236
      #if TraceIterators
237
        printf ( "    Moved up to %s, stage = %s\n",
238
                 info.currPos->fullPath.c_str(), sStageNames[info.currPos->visitStage] );
239
      #endif
240
    
241
0
    } else {
242
      
243
      // -------------------------------------------------------------------------------------------
244
      // Decide what to do with this iteration node based on its state. Don't use a switch statement,
245
      // some of the cases want to break from the loop. A break in a switch just exits the case.
246
      
247
      #if TraceIterators
248
        printf ( "    Moving from %s, stage = %s\n",
249
                 info.currPos->fullPath.c_str(), sStageNames[info.currPos->visitStage] );
250
      #endif
251
      
252
0
      if ( info.currPos->visitStage == kIter_BeforeVisit ) {   // Visit this node now.
253
0
        if ( info.currPos->options & kXMP_SchemaNode ) SetCurrSchema ( info, info.currPos->fullPath );
254
0
        break;
255
0
      }
256
257
0
      if ( info.currPos->visitStage == kIter_VisitSelf ) {   // Just finished visiting the value portion.
258
0
        info.currPos->visitStage = kIter_VisitQualifiers;   // Start visiting the qualifiers.
259
0
        if ( ! info.currPos->qualifiers.empty() ) {
260
0
          info.ancestors.push_back ( IterPosPair ( info.currPos, info.endPos ) );
261
0
          info.endPos  = info.currPos->qualifiers.end();    // ! Set the parent's endPos before changing currPos!
262
0
          info.currPos = info.currPos->qualifiers.begin();
263
0
          break;
264
0
        }
265
0
      }
266
267
0
      if ( info.currPos->visitStage == kIter_VisitQualifiers ) { // Just finished visiting the qualifiers.
268
0
        info.currPos->qualifiers.clear();
269
0
        info.currPos->visitStage = kIter_VisitChildren;     // Start visiting the children.
270
0
        if ( ! info.currPos->children.empty() ) {
271
0
          info.ancestors.push_back ( IterPosPair ( info.currPos, info.endPos ) );
272
0
          info.endPos  = info.currPos->children.end();    // ! Set the parent's endPos before changing currPos!
273
0
          info.currPos = info.currPos->children.begin();
274
0
          break;
275
0
        }
276
0
      }
277
278
0
      if ( info.currPos->visitStage == kIter_VisitChildren ) { // Just finished visiting the children.
279
0
        info.currPos->children.clear();
280
0
        ++info.currPos;                     // Move to the next sibling.
281
0
        continue;
282
0
      }
283
      
284
      #if TraceIterators
285
        if ( info.currPos != info.endPos ) {
286
          printf ( "    Moved to %s, stage = %s\n",
287
                   info.currPos->fullPath.c_str(), sStageNames[info.currPos->visitStage] );
288
        }
289
      #endif
290
      
291
0
    }
292
293
0
  } // Loop to find the next node.
294
  
295
0
  XMP_Assert ( (info.currPos == info.endPos) || (info.currPos->visitStage == kIter_BeforeVisit) );
296
297
0
}  // AdvanceIterPos
298
299
// -------------------------------------------------------------------------------------------------
300
// GetNextXMPNode
301
// --------------
302
//
303
// Used by XMPIterator::Next to obtain the next XMP node, ignoring the kXMP_IterJustLeafNodes flag.
304
// This isolates some messy code, allowing a clean loop in Next if kXMP_IterJustLeafNodes is set.
305
306
static const XMP_Node *
307
GetNextXMPNode ( IterInfo & info )
308
0
{
309
0
  const XMP_Node * xmpNode = 0;
310
311
  // ----------------------------------------------------------------------------------------------
312
  // On entry currPos points to an iteration node whose state is either before-visit or visit-self.
313
  // If it is before-visit then we will return that node's value part now. If it is visit-self it
314
  // means the previous iteration returned the value portion of that node, so we can advance to the
315
  // next node in the iteration tree. Then we find the corresponding XMP node, allowing for the XMP
316
  // tree to have been modified since that part of the iteration tree was constructed.
317
  
318
  // ! NOTE: Supporting aliases throws in some nastiness with schemas. There might not be any XMP
319
  // ! node for the schema, but we still have to visit it because of possible aliases. The static
320
  // ! sDummySchema is returned if there is no real schema node.
321
322
0
  if ( info.currPos->visitStage != kIter_BeforeVisit ) AdvanceIterPos ( info );
323
  
324
0
  bool isSchemaNode = false;
325
0
  XMP_ExpandedXPath expPath;  // Keep outside the loop to avoid constant construct/destruct.
326
  
327
0
  while ( info.currPos != info.endPos ) {
328
329
0
    isSchemaNode = XMP_NodeIsSchema ( info.currPos->options );
330
0
    if ( isSchemaNode ) {
331
0
      SetCurrSchema ( info, info.currPos->fullPath );
332
0
      xmpNode = FindConstSchema ( &info.xmpObj->tree, info.currPos->fullPath.c_str() );
333
0
      if ( xmpNode == 0 ) xmpNode = sDummySchema;
334
0
    } else {
335
0
      ExpandXPath ( info.currSchema.c_str(), info.currPos->fullPath.c_str(), &expPath );
336
0
      xmpNode = FindConstNode ( &info.xmpObj->tree, expPath );
337
0
    }
338
0
    if ( xmpNode != 0 ) break; // Exit the loop, we found a live XMP node.
339
340
0
    info.currPos->visitStage = kIter_VisitChildren; // Make AdvanceIterPos move to the next sibling.
341
0
    info.currPos->children.clear();
342
0
    info.currPos->qualifiers.clear();
343
0
    AdvanceIterPos ( info );
344
345
0
  }
346
347
0
  if ( info.currPos == info.endPos ) return 0;
348
  
349
  // -------------------------------------------------------------------------------------------
350
  // Now we've got the iteration node and corresponding XMP node. Add the iteration children for
351
  // structs and arrays. The children of schema were added when the iterator was constructed.
352
353
0
  XMP_Assert ( info.currPos->visitStage == kIter_BeforeVisit );
354
355
0
  if ( info.currPos->visitStage == kIter_BeforeVisit ) {
356
0
    if ( (! isSchemaNode) && (! (info.options & kXMP_IterJustChildren)) ) {
357
0
      AddNodeOffspring ( info, *info.currPos, xmpNode );
358
0
    }
359
0
    info.currPos->visitStage = kIter_VisitSelf;
360
0
  }
361
  
362
0
  return xmpNode;
363
364
0
}  // GetNextXMPNode
365
366
// =================================================================================================
367
// Init/Term
368
// =================================================================================================
369
370
// -------------------------------------------------------------------------------------------------
371
// Initialize
372
// ----------
373
374
/* class static */ bool
375
XMPIterator::Initialize()
376
1
{
377
1
  sDummySchema = new XMP_Node ( 0, "dummy:schema/", kXMP_SchemaNode);
378
1
  return true;
379
  
380
1
}  // Initialize
381
382
// -------------------------------------------------------------------------------------------------
383
// Terminate
384
// ----------
385
386
/* class static */ void
387
XMPIterator::Terminate() RELEASE_NO_THROW
388
18.5k
{
389
18.5k
  delete ( sDummySchema );
390
18.5k
  sDummySchema = 0;
391
18.5k
  return;
392
  
393
18.5k
}  // Terminate
394
395
// -------------------------------------------------------------------------------------------------
396
// Unlock
397
// ------
398
399
void
400
XMPIterator::Unlock ( XMP_OptionBits options )
401
0
{
402
0
  UNUSED(options);
403
404
0
  XMPMeta::Unlock ( 0 );
405
  
406
0
}  // Unlock
407
408
// =================================================================================================
409
// Constructors
410
// =================================================================================================
411
412
// -------------------------------------------------------------------------------------------------
413
// XMPIterator
414
// -----------
415
//
416
// Constructor for iterations over the nodes in an XMPMeta object. This builds a tree of iteration
417
// nodes that caches the existing node names of the XMPMeta object. The iteration tree is a partial
418
// replica of the XMPMeta tree. The initial iteration tree normally has just the root node, all of
419
// the schema nodes for a full object iteration. Lower level nodes (children and qualifiers) are 
420
// added when the parent is visited. If the kXMP_IterJustChildren option is passed then the initial
421
// iterator includes the children and the parent is marked as done. The iteration tree nodes are
422
// pruned when they are no longer needed. 
423
424
XMPIterator::XMPIterator ( const XMPMeta & xmpObj,
425
               XMP_StringPtr   schemaNS,
426
               XMP_StringPtr   propName,
427
0
               XMP_OptionBits  options ) : clientRefs(0), info(IterInfo(options,&xmpObj))
428
0
{
429
0
  if ( (options & kXMP_IterClassMask) != kXMP_IterProperties ) {
430
0
    XMP_Throw ( "Unsupported iteration kind", kXMPErr_BadOptions );
431
0
  }
432
  
433
  // *** Lock the XMPMeta object if we ever stop using a full DLL lock.
434
435
0
  if ( *propName != 0 ) {
436
437
    // An iterator rooted at a specific node.
438
439
    #if TraceIterators
440
      printf ( "\nNew XMP property iterator for \"%s\", options = %X\n    Schema = %s, root = %s\n",
441
               xmpObj.tree.name.c_str(), options, schemaNS, propName );
442
    #endif
443
    
444
0
    XMP_ExpandedXPath propPath;
445
0
    ExpandXPath ( schemaNS, propName, &propPath );
446
0
    XMP_Node * propNode = FindConstNode ( &xmpObj.tree, propPath );  // If not found get empty iteration.
447
    
448
0
    if ( propNode != 0 ) {
449
450
0
      XMP_VarString rootName ( propPath[1].step );  // The schema is [0].
451
0
      for ( size_t i = 2; i < propPath.size(); ++i ) {
452
0
        XMP_OptionBits stepKind = GetStepKind ( propPath[i].options );
453
0
        if ( stepKind <= kXMP_QualifierStep ) rootName += '/';
454
0
        rootName += propPath[i].step;
455
0
      }
456
457
0
      propName = rootName.c_str();
458
0
      size_t leafOffset = rootName.size();
459
0
      while ( (leafOffset > 0) && (propName[leafOffset] != '/') && (propName[leafOffset] != '[') ) --leafOffset;
460
0
      if ( propName[leafOffset] == '/' ) ++leafOffset;
461
462
0
      info.tree.children.push_back ( IterNode ( propNode->options, propName, leafOffset ) );
463
0
      SetCurrSchema ( info, propPath[kSchemaStep].step.c_str() );
464
0
      if ( info.options & kXMP_IterJustChildren ) {
465
0
        AddNodeOffspring ( info, info.tree.children.back(), propNode );
466
0
      }
467
468
0
    }
469
  
470
0
  } else if ( *schemaNS != 0 ) {
471
472
    // An iterator for all properties in one schema.
473
    
474
    #if TraceIterators
475
      printf ( "\nNew XMP schema iterator for \"%s\", options = %X\n    Schema = %s\n",
476
               xmpObj.tree.name.c_str(), options, schemaNS );
477
    #endif
478
    
479
0
    info.tree.children.push_back ( IterNode ( kXMP_SchemaNode, schemaNS, 0 ) );
480
0
    IterNode & iterSchema = info.tree.children.back();
481
    
482
0
    XMP_Node * xmpSchema = FindConstSchema ( &xmpObj.tree, schemaNS );
483
0
    if ( xmpSchema != 0 ) AddSchemaProps ( info, iterSchema, xmpSchema );
484
    
485
0
    if ( info.options & kXMP_IterIncludeAliases ) AddSchemaAliases ( info, iterSchema, schemaNS );
486
    
487
0
    if ( iterSchema.children.empty() ) {
488
0
      info.tree.children.pop_back();  // No properties, remove the schema node.
489
0
    } else {
490
0
      SetCurrSchema ( info, schemaNS );
491
0
    }
492
  
493
0
  } else {
494
495
    // An iterator for all properties in all schema. First add schema that exist (have children),
496
    // adding aliases from them if appropriate. Then add schema that have no actual properties
497
    // but do have aliases to existing properties, if we're including aliases in the iteration.
498
    
499
    #if TraceIterators
500
      printf ( "\nNew XMP tree iterator for \"%s\", options = %X\n",
501
               xmpObj.tree.name.c_str(), options );
502
    #endif
503
    
504
    // First pick up the schema that exist.
505
    
506
0
    for ( size_t schemaNum = 0, schemaLim = xmpObj.tree.children.size(); schemaNum != schemaLim; ++schemaNum ) {
507
508
0
      const XMP_Node * xmpSchema = xmpObj.tree.children[schemaNum];
509
0
      info.tree.children.push_back ( IterNode ( kXMP_SchemaNode, xmpSchema->name, 0 ) );
510
0
      IterNode & iterSchema = info.tree.children.back();
511
512
0
      if ( ! (info.options & kXMP_IterJustChildren) ) {
513
0
        AddSchemaProps ( info, iterSchema, xmpSchema );
514
0
        if ( info.options & kXMP_IterIncludeAliases ) AddSchemaAliases ( info, iterSchema, xmpSchema->name.c_str() );
515
0
        if ( iterSchema.children.empty() ) info.tree.children.pop_back(); // No properties, remove the schema node.
516
0
      }
517
518
0
    }
519
    
520
0
    if ( info.options & kXMP_IterIncludeAliases ) {
521
522
      // Add the schema that only have aliases. The most convenient, and safest way, is to go
523
      // through the registered namespaces, see if it exists, and let AddSchemaAliases do its
524
      // thing if not. Don't combine with the above loop, it is nicer to have the "real" stuff
525
      // be in storage order (not subject to the namespace map order).
526
      
527
      // ! We don't do the kXMP_IterJustChildren handing in the same way here as above. The
528
      // ! existing schema (presumably) have actual children. We need to call AddSchemaAliases
529
      // ! here to determine if the namespace has any aliases to existing properties. We then
530
      // ! strip the children if necessary.
531
532
0
      XMP_cStringMapPos currNS = sNamespaceURIToPrefixMap->begin();
533
0
      XMP_cStringMapPos endNS  = sNamespaceURIToPrefixMap->end();
534
0
      for ( ; currNS != endNS; ++currNS ) {
535
0
        XMP_StringPtr schemaName = currNS->first.c_str();
536
0
        if ( FindConstSchema ( &xmpObj.tree, schemaName ) != 0 ) continue;
537
0
        info.tree.children.push_back ( IterNode ( kXMP_SchemaNode, schemaName, 0 ) );
538
0
        IterNode & iterSchema = info.tree.children.back();
539
0
        AddSchemaAliases ( info, iterSchema, schemaName );
540
0
        if ( iterSchema.children.empty() ) {
541
0
          info.tree.children.pop_back();  // No aliases, remove the schema node.
542
0
        } else if ( info.options & kXMP_IterJustChildren ) {
543
0
          iterSchema.children.clear();  // Get rid of the children.
544
0
        }
545
0
      }
546
547
0
    }
548
549
0
  }
550
  
551
  // Set the current iteration position to the first node to be visited.
552
  
553
0
  info.currPos = info.tree.children.begin();
554
0
  info.endPos  = info.tree.children.end();
555
  
556
0
  if ( (info.options & kXMP_IterJustChildren) && (info.currPos != info.endPos) && (*schemaNS != 0) ) {
557
0
    info.currPos->visitStage = kIter_VisitSelf;
558
0
  }
559
560
  #if TraceIterators
561
    if ( info.currPos == info.endPos ) {
562
      printf ( "    ** Empty iteration **\n" );
563
    } else {
564
      printf ( "    Initial node %s, stage = %s, iterator @ %.8X\n",
565
               info.currPos->fullPath.c_str(), sStageNames[info.currPos->visitStage], this );
566
    }
567
  #endif
568
  
569
0
}  // XMPIterator for XMPMeta objects
570
571
// -------------------------------------------------------------------------------------------------
572
// XMPIterator
573
// -----------
574
//
575
// Constructor for iterations over global tables such as registered namespaces or aliases.
576
577
XMPIterator::XMPIterator ( XMP_StringPtr  /*schemaNS*/,
578
                           XMP_StringPtr  /*propName*/,
579
0
                           XMP_OptionBits options ) : clientRefs(0), info(IterInfo(options,0))
580
0
{
581
582
0
  XMP_Throw ( "Unimplemented XMPIterator constructor for global tables", kXMPErr_Unimplemented );
583
584
0
}  // XMPIterator for global tables
585
586
// -------------------------------------------------------------------------------------------------
587
// ~XMPIterator
588
// -----------
589
590
XMPIterator::~XMPIterator() RELEASE_NO_THROW
591
0
{
592
0
  XMP_Assert ( this->clientRefs <= 0 );
593
  // Let everything else default.
594
  
595
0
}  // ~XMPIterator
596
597
// =================================================================================================
598
// Iteration Methods
599
// =================================================================================================
600
601
// -------------------------------------------------------------------------------------------------
602
// Next
603
// ----
604
//
605
// Do a preorder traversal of the cached nodes.
606
607
// *** Need to document the relationships between currPos, endPos, and visitStage.
608
609
bool
610
XMPIterator::Next ( XMP_StringPtr *  schemaNS,
611
          XMP_StringLen *  nsSize,
612
          XMP_StringPtr *  propPath,
613
          XMP_StringLen *  pathSize,
614
          XMP_StringPtr *  propValue,
615
          XMP_StringLen *  valueSize,
616
          XMP_OptionBits * propOptions )
617
0
{
618
  // *** Lock the XMPMeta object if we ever stop using a full DLL lock.
619
  
620
  // ! NOTE: Supporting aliases throws in some nastiness with schemas. There might not be any XMP
621
  // ! node for the schema, but we still have to visit it because of possible aliases.
622
  
623
0
  if ( info.currPos == info.endPos ) return false; // Happens at the start of an empty iteration.
624
  
625
  #if TraceIterators
626
    printf ( "Next iteration from %s, stage = %s, iterator @ %.8X\n",
627
           info.currPos->fullPath.c_str(), sStageNames[info.currPos->visitStage], this );
628
  #endif
629
  
630
0
  const XMP_Node * xmpNode = GetNextXMPNode ( info );
631
0
  if ( xmpNode == 0 ) return false;
632
0
  bool isSchemaNode = XMP_NodeIsSchema ( info.currPos->options );
633
  
634
0
  if ( info.options & kXMP_IterJustLeafNodes ) {
635
0
    while ( isSchemaNode || (! xmpNode->children.empty()) ) {
636
0
      info.currPos->visitStage = kIter_VisitQualifiers; // Skip to this node's children.
637
0
      xmpNode = GetNextXMPNode ( info );
638
0
      if ( xmpNode == 0 ) return false;
639
0
      isSchemaNode = XMP_NodeIsSchema ( info.currPos->options );
640
0
    }
641
0
  }
642
  
643
0
  *schemaNS = info.currSchema.c_str();
644
0
  *nsSize   = info.currSchema.size();
645
646
0
  *propOptions = info.currPos->options;
647
648
0
  *propPath  = "";
649
0
  *pathSize  = 0;
650
0
  *propValue = "";
651
0
  *valueSize = 0;
652
  
653
0
  if ( ! (*propOptions & kXMP_SchemaNode) ) {
654
655
0
    *propPath = info.currPos->fullPath.c_str();
656
0
    *pathSize = info.currPos->fullPath.size();
657
0
    if ( info.options & kXMP_IterJustLeafName ) {
658
0
      *propPath += info.currPos->leafOffset;
659
0
      *pathSize -= info.currPos->leafOffset;
660
0
    }
661
    
662
0
    if ( ! (*propOptions & kXMP_PropCompositeMask) ) {
663
0
      *propValue = xmpNode->value.c_str();
664
0
      *valueSize = xmpNode->value.size();
665
0
    }
666
667
0
  }
668
  
669
  #if TraceIterators
670
    printf ( "    Next node %s, stage = %s\n",
671
           info.currPos->fullPath.c_str(), sStageNames[info.currPos->visitStage] );
672
  #endif
673
  
674
0
  return true;
675
676
0
}  // Next
677
678
// -------------------------------------------------------------------------------------------------
679
// Skip
680
// ----
681
//
682
// Skip some portion of the traversal related to the last visited node. We skip either that node's
683
// children, or those children and the previous node's siblings. The implementation might look a bit
684
// awkward because info.currNode always points to the next node to be visited. We might already have
685
// moved past the things to skip, e.g. if the previous node was simple and the last of its siblings.
686
687
enum {
688
  kXMP_ValidIterSkipOptions = kXMP_IterSkipSubtree | kXMP_IterSkipSiblings
689
};
690
691
void
692
XMPIterator::Skip ( XMP_OptionBits iterOptions )
693
0
{
694
//  if ( (info.currPos == kIter_NullPos) )  XMP_Throw ( "No prior postion to skip from", kXMPErr_BadIterPosition );
695
0
  if ( iterOptions == 0 ) XMP_Throw ( "Must specify what to skip", kXMPErr_BadOptions );
696
0
  if ( (iterOptions & ~kXMP_ValidIterSkipOptions) != 0 ) XMP_Throw ( "Undefined options", kXMPErr_BadOptions );
697
698
  #if TraceIterators
699
    printf ( "Skipping from %s, stage = %s, iterator @ %.8X",
700
           info.currPos->fullPath.c_str(), sStageNames[info.currPos->visitStage], this );
701
  #endif
702
  
703
0
  if ( iterOptions & kXMP_IterSkipSubtree ) {
704
    #if TraceIterators
705
      printf ( ", mode = subtree\n" );
706
    #endif
707
0
    info.currPos->visitStage = kIter_VisitChildren;
708
0
  } else if ( iterOptions & kXMP_IterSkipSiblings ) {
709
    #if TraceIterators
710
      printf ( ", mode = siblings\n" );
711
    #endif
712
0
    info.currPos = info.endPos;
713
0
    AdvanceIterPos ( info );
714
0
  }
715
  #if TraceIterators
716
    printf ( "    Skipped to %s, stage = %s\n",
717
           info.currPos->fullPath.c_str(), sStageNames[info.currPos->visitStage] );
718
  #endif
719
  
720
721
0
}  // Skip
722
723
// -------------------------------------------------------------------------------------------------
724
// UnlockIter
725
// ----------
726
727
void
728
XMPIterator::UnlockIter ( XMP_OptionBits options )
729
0
{
730
0
  UNUSED(options);
731
732
0
  XMPMeta::Unlock ( 0 );
733
  
734
0
}  // UnlockIter
735
736
// =================================================================================================