summaryrefslogtreecommitdiff
path: root/gpr/source/lib/xmp_core/XMPIterator.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'gpr/source/lib/xmp_core/XMPIterator.cpp')
-rw-r--r--gpr/source/lib/xmp_core/XMPIterator.cpp637
1 files changed, 637 insertions, 0 deletions
diff --git a/gpr/source/lib/xmp_core/XMPIterator.cpp b/gpr/source/lib/xmp_core/XMPIterator.cpp
new file mode 100644
index 0000000..229cd8f
--- /dev/null
+++ b/gpr/source/lib/xmp_core/XMPIterator.cpp
@@ -0,0 +1,637 @@
+// =================================================================================================
+// Copyright 2003 Adobe Systems Incorporated
+// All Rights Reserved.
+//
+// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
+// of the Adobe license agreement accompanying it.
+// =================================================================================================
+
+#include "public/include/XMP_Environment.h" // ! This must be the first include!
+#include "XMPCore_Impl.hpp"
+
+#include "XMPIterator.hpp"
+
+#include <string>
+#include <stdio.h> // For snprintf.
+
+#if XMP_WinBuild
+ #pragma warning ( disable : 4702 ) // unreachable code
+ #pragma warning ( disable : 4800 ) // forcing value to bool 'true' or 'false' (performance warning)
+ #pragma warning ( disable : 4996 ) // '...' was declared deprecated
+#endif
+
+// =================================================================================================
+// Support Routines
+// =================================================================================================
+
+
+#ifndef TraceIterators
+ #define TraceIterators 0
+#endif
+
+#if TraceIterators
+ static const char * sStageNames[] = { "before", "self", "qualifiers", "children" };
+#endif
+
+static XMP_Node * sDummySchema = 0; // ! Used for some ugliness with aliases.
+
+// -------------------------------------------------------------------------------------------------
+// AddSchemaProps
+// --------------
+//
+// Add the top level properties to the IterNode for a schema.
+
+static void
+AddSchemaProps ( IterInfo & info, IterNode & iterSchema, const XMP_Node * xmpSchema )
+{
+ #if TraceIterators
+ printf ( " Adding properties of %s\n", xmpSchema->name.c_str() );
+ #endif
+
+ for ( size_t propNum = 0, propLim = xmpSchema->children.size(); propNum != propLim; ++propNum ) {
+ const XMP_Node * xmpProp = xmpSchema->children[propNum];
+ // *** set the has-aliases bit when appropriate
+ iterSchema.children.push_back ( IterNode ( xmpProp->options, xmpProp->name, 0 ) );
+ #if TraceIterators
+ printf ( " %s\n", xmpProp->name.c_str() );
+ #endif
+ }
+
+} // AddSchemaProps
+
+// -------------------------------------------------------------------------------------------------
+// AddNodeOffspring
+// ----------------
+//
+// Add the immediate children and qualifiers to an IterNode.
+
+static void
+AddNodeOffspring ( IterInfo & info, IterNode & iterParent, const XMP_Node * xmpParent )
+{
+ XMP_VarString currPath ( iterParent.fullPath );
+ size_t leafOffset = iterParent.fullPath.size();
+
+ if ( (! xmpParent->qualifiers.empty()) && (! (info.options & kXMP_IterOmitQualifiers)) ) {
+
+ #if TraceIterators
+ printf ( " Adding qualifiers of %s\n", currPath.c_str() );
+ #endif
+
+ currPath += "/?"; // All qualifiers are named and use paths like "Prop/?Qual".
+ leafOffset += 2;
+
+ for ( size_t qualNum = 0, qualLim = xmpParent->qualifiers.size(); qualNum != qualLim; ++qualNum ) {
+ const XMP_Node * xmpQual = xmpParent->qualifiers[qualNum];
+ currPath += xmpQual->name;
+ iterParent.qualifiers.push_back ( IterNode ( xmpQual->options, currPath, leafOffset ) );
+ currPath.erase ( leafOffset );
+ #if TraceIterators
+ printf ( " %s\n", xmpQual->name.c_str() );
+ #endif
+ }
+
+ leafOffset -= 2;
+ currPath.erase ( leafOffset );
+
+ }
+
+ if ( ! xmpParent->children.empty() ) {
+
+ #if TraceIterators
+ printf ( " Adding children of %s\n", currPath.c_str() );
+ #endif
+
+ XMP_Assert ( xmpParent->options & kXMP_PropCompositeMask );
+
+ if ( xmpParent->options & kXMP_PropValueIsStruct ) {
+ currPath += '/';
+ leafOffset += 1;
+ }
+
+ for ( size_t childNum = 0, childLim = xmpParent->children.size(); childNum != childLim; ++childNum ) {
+ const XMP_Node * xmpChild = xmpParent->children[childNum];
+ if ( ! (xmpParent->options & kXMP_PropValueIsArray) ) {
+ currPath += xmpChild->name;
+ } else {
+ char buffer [32]; // AUDIT: Using sizeof(buffer) below for snprintf length is safe.
+ snprintf ( buffer, sizeof(buffer), "[%lu]", childNum+1 ); // ! XPath indices are one-based.
+ currPath += buffer;
+ }
+ iterParent.children.push_back ( IterNode ( xmpChild->options, currPath, leafOffset ) );
+ currPath.erase ( leafOffset );
+ #if TraceIterators
+ printf ( " %s\n", (iterParent.children.back().fullPath.c_str() + leafOffset) );
+ #endif
+ }
+
+ }
+
+} // AddNodeOffspring
+
+// -------------------------------------------------------------------------------------------------
+// SetCurrSchema
+// -------------
+
+static inline void
+SetCurrSchema ( IterInfo & info, XMP_StringPtr schemaName )
+{
+
+ info.currSchema = schemaName;
+ #if 0 // *** XMP_DebugBuild
+ info._schemaPtr = info.currSchema.c_str();
+ #endif
+
+} // SetCurrSchema
+
+static inline void
+SetCurrSchema ( IterInfo & info, XMP_VarString & schemaName )
+{
+
+ info.currSchema = schemaName;
+ #if 0 // *** XMP_DebugBuild
+ info._schemaPtr = info.currSchema.c_str();
+ #endif
+
+} // SetCurrSchema
+
+// -------------------------------------------------------------------------------------------------
+// AdvanceIterPos
+// --------------
+//
+// Adjust currPos and possibly endPos for the next step in a pre-order depth-first traversal. The
+// current node has just been visited, move on to its qualifiers, children, then siblings, or back
+// up to an ancestor. AdvanceIterPos either moves to a property or qualifier node that can be
+// visited, or to the end of the entire iteration.
+
+static void
+AdvanceIterPos ( IterInfo & info )
+{
+ // -------------------------------------------------------------------------------------------
+ // Keep looking until we find a node to visit or the end of everything. The first time through
+ // the current node will exist, we just visited it. But we have to keep looking if the current
+ // node was the last of its siblings or is an empty schema.
+
+ // ! It is possible that info.currPos == info.endPos on entry. Don't dereference info.currPos yet!
+
+ while ( true ) {
+
+ if ( info.currPos == info.endPos ) {
+
+ // ------------------------------------------------------------------------------------
+ // At the end of a set of siblings, move up to an ancestor. We've either just finished
+ // the qualifiers and will move to the children, or have just finished the children and
+ // will move on to the next sibling.
+
+ if ( info.ancestors.empty() ) break; // We're at the end of the schema list.
+
+ IterPosPair & parent = info.ancestors.back();
+ info.currPos = parent.first;
+ info.endPos = parent.second;
+ info.ancestors.pop_back();
+
+ #if TraceIterators
+ printf ( " Moved up to %s, stage = %s\n",
+ info.currPos->fullPath.c_str(), sStageNames[info.currPos->visitStage] );
+ #endif
+
+ } else {
+
+ // -------------------------------------------------------------------------------------------
+ // Decide what to do with this iteration node based on its state. Don't use a switch statment,
+ // some of the cases want to break from the loop. A break in a switch just exits the case.
+
+ #if TraceIterators
+ printf ( " Moving from %s, stage = %s\n",
+ info.currPos->fullPath.c_str(), sStageNames[info.currPos->visitStage] );
+ #endif
+
+ if ( info.currPos->visitStage == kIter_BeforeVisit ) { // Visit this node now.
+ if ( info.currPos->options & kXMP_SchemaNode ) SetCurrSchema ( info, info.currPos->fullPath );
+ break;
+ }
+
+ if ( info.currPos->visitStage == kIter_VisitSelf ) { // Just finished visiting the value portion.
+ info.currPos->visitStage = kIter_VisitQualifiers; // Start visiting the qualifiers.
+ if ( ! info.currPos->qualifiers.empty() ) {
+ info.ancestors.push_back ( IterPosPair ( info.currPos, info.endPos ) );
+ info.endPos = info.currPos->qualifiers.end(); // ! Set the parent's endPos before changing currPos!
+ info.currPos = info.currPos->qualifiers.begin();
+ break;
+ }
+ }
+
+ if ( info.currPos->visitStage == kIter_VisitQualifiers ) { // Just finished visiting the qualifiers.
+ info.currPos->qualifiers.clear();
+ info.currPos->visitStage = kIter_VisitChildren; // Start visiting the children.
+ if ( ! info.currPos->children.empty() ) {
+ info.ancestors.push_back ( IterPosPair ( info.currPos, info.endPos ) );
+ info.endPos = info.currPos->children.end(); // ! Set the parent's endPos before changing currPos!
+ info.currPos = info.currPos->children.begin();
+ break;
+ }
+ }
+
+ if ( info.currPos->visitStage == kIter_VisitChildren ) { // Just finished visiting the children.
+ info.currPos->children.clear();
+ ++info.currPos; // Move to the next sibling.
+ continue;
+ }
+
+ #if TraceIterators
+ if ( info.currPos != info.endPos ) {
+ printf ( " Moved to %s, stage = %s\n",
+ info.currPos->fullPath.c_str(), sStageNames[info.currPos->visitStage] );
+ }
+ #endif
+
+ }
+
+ } // Loop to find the next node.
+
+ XMP_Assert ( (info.currPos == info.endPos) || (info.currPos->visitStage == kIter_BeforeVisit) );
+
+} // AdvanceIterPos
+
+// -------------------------------------------------------------------------------------------------
+// GetNextXMPNode
+// --------------
+//
+// Used by XMPIterator::Next to obtain the next XMP node, ignoring the kXMP_IterJustLeafNodes flag.
+// This isolates some messy code, allowing a clean loop in Next if kXMP_IterJustLeafNodes is set.
+
+static const XMP_Node *
+GetNextXMPNode ( IterInfo & info )
+{
+ const XMP_Node * xmpNode = 0;
+
+ // ----------------------------------------------------------------------------------------------
+ // On entry currPos points to an iteration node whose state is either before-visit or visit-self.
+ // If it is before-visit then we will return that node's value part now. If it is visit-self it
+ // means the previous iteration returned the value portion of that node, so we can advance to the
+ // next node in the iteration tree. Then we find the corresponding XMP node, allowing for the XMP
+ // tree to have been modified since that part of the iteration tree was constructed.
+
+ // ! NOTE: Supporting aliases throws in some nastiness with schemas. There might not be any XMP
+ // ! node for the schema, but we still have to visit it because of possible aliases. The static
+ // ! sDummySchema is returned if there is no real schema node.
+
+ if ( info.currPos->visitStage != kIter_BeforeVisit ) AdvanceIterPos ( info );
+
+ bool isSchemaNode = false;
+ XMP_ExpandedXPath expPath; // Keep outside the loop to avoid constant construct/destruct.
+
+ while ( info.currPos != info.endPos ) {
+
+ isSchemaNode = XMP_NodeIsSchema ( info.currPos->options );
+ if ( isSchemaNode ) {
+ SetCurrSchema ( info, info.currPos->fullPath );
+ xmpNode = FindConstSchema ( &info.xmpObj->tree, info.currPos->fullPath.c_str() );
+ if ( xmpNode == 0 ) xmpNode = sDummySchema;
+ } else {
+ ExpandXPath ( info.currSchema.c_str(), info.currPos->fullPath.c_str(), &expPath );
+ xmpNode = FindConstNode ( &info.xmpObj->tree, expPath );
+ }
+ if ( xmpNode != 0 ) break; // Exit the loop, we found a live XMP node.
+
+ info.currPos->visitStage = kIter_VisitChildren; // Make AdvanceIterPos move to the next sibling.
+ info.currPos->children.clear();
+ info.currPos->qualifiers.clear();
+ AdvanceIterPos ( info );
+
+ }
+
+ if ( info.currPos == info.endPos ) return 0;
+
+ // -------------------------------------------------------------------------------------------
+ // Now we've got the iteration node and corresponding XMP node. Add the iteration children for
+ // structs and arrays. The children of schema were added when the iterator was constructed.
+
+ XMP_Assert ( info.currPos->visitStage == kIter_BeforeVisit );
+
+ if ( info.currPos->visitStage == kIter_BeforeVisit ) {
+ if ( (! isSchemaNode) && (! (info.options & kXMP_IterJustChildren)) ) {
+ AddNodeOffspring ( info, *info.currPos, xmpNode );
+ }
+ info.currPos->visitStage = kIter_VisitSelf;
+ }
+
+ return xmpNode;
+
+} // GetNextXMPNode
+
+// =================================================================================================
+// Init/Term
+// =================================================================================================
+
+// -------------------------------------------------------------------------------------------------
+// Initialize
+// ----------
+
+/* class static */ bool
+XMPIterator::Initialize()
+{
+ sDummySchema = new XMP_Node ( 0, "dummy:schema/", kXMP_SchemaNode);
+ return true;
+
+} // Initialize
+
+// -------------------------------------------------------------------------------------------------
+// Terminate
+// ----------
+
+/* class static */ void
+XMPIterator::Terminate() RELEASE_NO_THROW
+{
+ delete ( sDummySchema );
+ sDummySchema = 0;
+ return;
+
+} // Terminate
+
+// =================================================================================================
+// Constructors
+// =================================================================================================
+
+// -------------------------------------------------------------------------------------------------
+// XMPIterator
+// -----------
+//
+// Constructor for iterations over the nodes in an XMPMeta object. This builds a tree of iteration
+// nodes that caches the existing node names of the XMPMeta object. The iteration tree is a partial
+// replica of the XMPMeta tree. The initial iteration tree normally has just the root node, all of
+// the schema nodes for a full object iteration. Lower level nodes (children and qualifiers) are
+// added when the parent is visited. If the kXMP_IterJustChildren option is passed then the initial
+// iterator includes the children and the parent is marked as done. The iteration tree nodes are
+// pruned when they are no longer needed.
+
+XMPIterator::XMPIterator ( const XMPMeta & xmpObj,
+ XMP_StringPtr schemaNS,
+ XMP_StringPtr propName,
+ XMP_OptionBits options ) : info(IterInfo(options,&xmpObj)), clientRefs(0)
+{
+ if ( (options & kXMP_IterClassMask) != kXMP_IterProperties ) {
+ XMP_Throw ( "Unsupported iteration kind", kXMPErr_BadOptions );
+ }
+
+ // *** Lock the XMPMeta object if we ever stop using a full DLL lock.
+
+ if ( *propName != 0 ) {
+
+ // An iterator rooted at a specific node.
+
+ #if TraceIterators
+ printf ( "\nNew XMP property iterator for \"%s\", options = %X\n Schema = %s, root = %s\n",
+ xmpObj.tree.name.c_str(), options, schemaNS, propName );
+ #endif
+
+ XMP_ExpandedXPath propPath;
+ ExpandXPath ( schemaNS, propName, &propPath );
+ XMP_Node * propNode = FindConstNode ( &xmpObj.tree, propPath ); // If not found get empty iteration.
+
+ if ( propNode != 0 ) {
+
+ XMP_VarString rootName ( propPath[1].step ); // The schema is [0].
+ for ( size_t i = 2; i < propPath.size(); ++i ) {
+ XMP_OptionBits stepKind = GetStepKind ( propPath[i].options );
+ if ( stepKind <= kXMP_QualifierStep ) rootName += '/';
+ rootName += propPath[i].step;
+ }
+
+ propName = rootName.c_str();
+ size_t leafOffset = rootName.size();
+ while ( (leafOffset > 0) && (propName[leafOffset] != '/') && (propName[leafOffset] != '[') ) --leafOffset;
+ if ( propName[leafOffset] == '/' ) ++leafOffset;
+
+ info.tree.children.push_back ( IterNode ( propNode->options, propName, leafOffset ) );
+ SetCurrSchema ( info, propPath[kSchemaStep].step.c_str() );
+ if ( info.options & kXMP_IterJustChildren ) {
+ AddNodeOffspring ( info, info.tree.children.back(), propNode );
+ }
+
+ }
+
+ } else if ( *schemaNS != 0 ) {
+
+ // An iterator for all properties in one schema.
+
+ #if TraceIterators
+ printf ( "\nNew XMP schema iterator for \"%s\", options = %X\n Schema = %s\n",
+ xmpObj.tree.name.c_str(), options, schemaNS );
+ #endif
+
+ info.tree.children.push_back ( IterNode ( kXMP_SchemaNode, schemaNS, 0 ) );
+ IterNode & iterSchema = info.tree.children.back();
+
+ XMP_Node * xmpSchema = FindConstSchema ( &xmpObj.tree, schemaNS );
+ if ( xmpSchema != 0 ) AddSchemaProps ( info, iterSchema, xmpSchema );
+
+ if ( iterSchema.children.empty() ) {
+ info.tree.children.pop_back(); // No properties, remove the schema node.
+ } else {
+ SetCurrSchema ( info, schemaNS );
+ }
+
+ } else {
+
+ // An iterator for all properties in all schema. First add schema that exist (have children),
+ // adding aliases from them if appropriate. Then add schema that have no actual properties
+ // but do have aliases to existing properties, if we're including aliases in the iteration.
+
+ #if TraceIterators
+ printf ( "\nNew XMP tree iterator for \"%s\", options = %X\n",
+ xmpObj.tree.name.c_str(), options );
+ #endif
+
+ // First pick up the schema that exist.
+
+ for ( size_t schemaNum = 0, schemaLim = xmpObj.tree.children.size(); schemaNum != schemaLim; ++schemaNum ) {
+
+ const XMP_Node * xmpSchema = xmpObj.tree.children[schemaNum];
+ info.tree.children.push_back ( IterNode ( kXMP_SchemaNode, xmpSchema->name, 0 ) );
+ IterNode & iterSchema = info.tree.children.back();
+
+ if ( ! (info.options & kXMP_IterJustChildren) ) {
+ AddSchemaProps ( info, iterSchema, xmpSchema );
+ if ( iterSchema.children.empty() ) info.tree.children.pop_back(); // No properties, remove the schema node.
+ }
+
+ }
+
+ }
+
+ // Set the current iteration position to the first node to be visited.
+
+ info.currPos = info.tree.children.begin();
+ info.endPos = info.tree.children.end();
+
+ if ( (info.options & kXMP_IterJustChildren) && (info.currPos != info.endPos) && (*schemaNS != 0) ) {
+ info.currPos->visitStage = kIter_VisitSelf;
+ }
+
+ #if TraceIterators
+ if ( info.currPos == info.endPos ) {
+ printf ( " ** Empty iteration **\n" );
+ } else {
+ printf ( " Initial node %s, stage = %s, iterator @ %.8X\n",
+ info.currPos->fullPath.c_str(), sStageNames[info.currPos->visitStage], this );
+ }
+ #endif
+
+} // XMPIterator for XMPMeta objects
+
+// -------------------------------------------------------------------------------------------------
+// XMPIterator
+// -----------
+//
+// Constructor for iterations over global tables such as registered namespaces or aliases.
+
+XMPIterator::XMPIterator ( XMP_StringPtr schemaNS,
+ XMP_StringPtr propName,
+ XMP_OptionBits options ) : info(IterInfo(options,0)), clientRefs(0)
+{
+
+ XMP_Throw ( "Unimplemented XMPIterator constructor for global tables", kXMPErr_Unimplemented );
+ void * p; p = &schemaNS; p = &propName; p = &options; // Avoid unused param warnings.
+
+} // XMPIterator for global tables
+
+// -------------------------------------------------------------------------------------------------
+// ~XMPIterator
+// -----------
+
+XMPIterator::~XMPIterator() RELEASE_NO_THROW
+{
+ XMP_Assert ( this->clientRefs <= 0 );
+ // Let everything else default.
+
+} // ~XMPIterator
+
+// =================================================================================================
+// Iteration Methods
+// =================================================================================================
+
+// -------------------------------------------------------------------------------------------------
+// Next
+// ----
+//
+// Do a preorder traversal of the cached nodes.
+
+// *** Need to document the relationships between currPos, endPos, and visitStage.
+
+bool
+XMPIterator::Next ( XMP_StringPtr * schemaNS,
+ XMP_StringLen * nsSize,
+ XMP_StringPtr * propPath,
+ XMP_StringLen * pathSize,
+ XMP_StringPtr * propValue,
+ XMP_StringLen * valueSize,
+ XMP_OptionBits * propOptions )
+{
+ // *** Lock the XMPMeta object if we ever stop using a full DLL lock.
+
+ // ! NOTE: Supporting aliases throws in some nastiness with schemas. There might not be any XMP
+ // ! node for the schema, but we still have to visit it because of possible aliases.
+
+ if ( info.currPos == info.endPos ) return false; // Happens at the start of an empty iteration.
+
+ #if TraceIterators
+ printf ( "Next iteration from %s, stage = %s, iterator @ %.8X\n",
+ info.currPos->fullPath.c_str(), sStageNames[info.currPos->visitStage], this );
+ #endif
+
+ const XMP_Node * xmpNode = GetNextXMPNode ( info );
+ if ( xmpNode == 0 ) return false;
+ bool isSchemaNode = XMP_NodeIsSchema ( info.currPos->options );
+
+ if ( info.options & kXMP_IterJustLeafNodes ) {
+ while ( isSchemaNode || (! xmpNode->children.empty()) ) {
+ info.currPos->visitStage = kIter_VisitQualifiers; // Skip to this node's children.
+ xmpNode = GetNextXMPNode ( info );
+ if ( xmpNode == 0 ) return false;
+ isSchemaNode = XMP_NodeIsSchema ( info.currPos->options );
+ }
+ }
+
+ *schemaNS = info.currSchema.c_str();
+ *nsSize = info.currSchema.size();
+
+ *propOptions = info.currPos->options;
+
+ *propPath = "";
+ *pathSize = 0;
+ *propValue = "";
+ *valueSize = 0;
+
+ if ( ! (*propOptions & kXMP_SchemaNode) ) {
+
+ *propPath = info.currPos->fullPath.c_str();
+ *pathSize = info.currPos->fullPath.size();
+
+ if ( info.options & kXMP_IterJustLeafName ) {
+ *propPath += info.currPos->leafOffset;
+ *pathSize -= info.currPos->leafOffset;
+ xmpNode->GetLocalURI ( schemaNS, nsSize ); // Use the leaf namespace, not the top namespace.
+ }
+
+ if ( ! (*propOptions & kXMP_PropCompositeMask) ) {
+ *propValue = xmpNode->value.c_str();
+ *valueSize = xmpNode->value.size();
+ }
+
+ }
+
+ #if TraceIterators
+ printf ( " Next node %s, stage = %s\n",
+ info.currPos->fullPath.c_str(), sStageNames[info.currPos->visitStage] );
+ #endif
+
+ return true;
+
+} // Next
+
+// -------------------------------------------------------------------------------------------------
+// Skip
+// ----
+//
+// Skip some portion of the traversal related to the last visited node. We skip either that node's
+// children, or those children and the previous node's siblings. The implementation might look a bit
+// awkward because info.currNode always points to the next node to be visited. We might already have
+// moved past the things to skip, e.g. if the previous node was simple and the last of its siblings.
+
+enum {
+ kXMP_ValidIterSkipOptions = kXMP_IterSkipSubtree | kXMP_IterSkipSiblings
+};
+
+void
+XMPIterator::Skip ( XMP_OptionBits iterOptions )
+{
+// if ( (info.currPos == kIter_NullPos) ) XMP_Throw ( "No prior postion to skip from", kXMPErr_BadIterPosition );
+ if ( iterOptions == 0 ) XMP_Throw ( "Must specify what to skip", kXMPErr_BadOptions );
+ if ( (iterOptions & ~kXMP_ValidIterSkipOptions) != 0 ) XMP_Throw ( "Undefined options", kXMPErr_BadOptions );
+
+ #if TraceIterators
+ printf ( "Skipping from %s, stage = %s, iterator @ %.8X",
+ info.currPos->fullPath.c_str(), sStageNames[info.currPos->visitStage], this );
+ #endif
+
+ if ( iterOptions & kXMP_IterSkipSubtree ) {
+ #if TraceIterators
+ printf ( ", mode = subtree\n" );
+ #endif
+ info.currPos->visitStage = kIter_VisitChildren;
+ } else if ( iterOptions & kXMP_IterSkipSiblings ) {
+ #if TraceIterators
+ printf ( ", mode = siblings\n" );
+ #endif
+ info.currPos = info.endPos;
+ AdvanceIterPos ( info );
+ }
+ #if TraceIterators
+ printf ( " Skipped to %s, stage = %s\n",
+ info.currPos->fullPath.c_str(), sStageNames[info.currPos->visitStage] );
+ #endif
+
+
+} // Skip
+
+// =================================================================================================