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