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