Blame xmpsdk/src/XMPIterator.cpp

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