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
XMPIterator::XMPIterator ( const XMPMeta & xmpObj, XMP_StringPtr schemaNS, XMP_StringPtr propName, XMP_OptionBits options ) : clientRefs(0), info(IterInfo(options,&xmpObj)) { 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 ( info.options & kXMP_IterIncludeAliases ) AddSchemaAliases ( info, iterSchema, schemaNS ); 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 ( info.options & kXMP_IterIncludeAliases ) AddSchemaAliases ( info, iterSchema, xmpSchema->name.c_str() ); if ( iterSchema.children.empty() ) info.tree.children.pop_back(); // No properties, remove the schema node. } } if ( info.options & kXMP_IterIncludeAliases ) { // Add the schema that only have aliases. The most convenient, and safest way, is to go // through the registered namespaces, see if it exists, and let AddSchemaAliases do its // thing if not. Don't combine with the above loop, it is nicer to have the "real" stuff // be in storage order (not subject to the namespace map order). // ! We don't do the kXMP_IterJustChildren handing in the same way here as above. The // ! existing schema (presumably) have actual children. We need to call AddSchemaAliases // ! here to determine if the namespace has any aliases to existing properties. We then // ! strip the children if necessary. XMP_cStringMapPos currNS = sNamespaceURIToPrefixMap->begin(); XMP_cStringMapPos endNS = sNamespaceURIToPrefixMap->end(); for ( ; currNS != endNS; ++currNS ) { XMP_StringPtr schemaName = currNS->first.c_str(); if ( FindConstSchema ( &xmpObj.tree, schemaName ) != 0 ) continue; info.tree.children.push_back ( IterNode ( kXMP_SchemaNode, schemaName, 0 ) ); IterNode & iterSchema = info.tree.children.back(); AddSchemaAliases ( info, iterSchema, schemaName ); if ( iterSchema.children.empty() ) { info.tree.children.pop_back(); // No aliases, remove the schema node. } else if ( info.options & kXMP_IterJustChildren ) { iterSchema.children.clear(); // Get rid of the children. } } } } // 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::XMPIterator ( const XMPMeta & xmpObj, XMP_StringPtr schemaNS, XMP_StringPtr propName, XMP_OptionBits options ) : clientRefs(0), info(IterInfo(options,&xmpObj)) { 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