1 // =================================================================================================
2 // Copyright 2003 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 
10 #include "public/include/XMP_Environment.h"	// ! This must be the first include!
11 #include "XMPCore/source/XMPCore_Impl.hpp"
12 
13 #include "XMPCore/XMPCoreDefines.h"
14 #if ENABLE_CPP_DOM_MODEL
15 #include "XMPCore/source/XMPIterator2.hpp"
16 #include "XMPCore/source/XMPMeta2.hpp"
17 #include "XMPCore/source/XMPUtils.hpp"
18 #include "XMPCore/Interfaces/IMetadata_I.h"
19 #include "XMPCore/Interfaces/INameSpacePrefixMap_I.h"
20 #include "XMPCommon/Interfaces/IUTF8String_I.h"
21 #include "XMPCore/Interfaces/ISimpleNode_I.h"
22 #include <map>
23 #include <string>
24 #include <stdio.h>	// For snprintf.
25 
26 #if XMP_WinBuild
27 	#pragma warning ( disable : 4702 )	// unreachable code
28 	#pragma warning ( disable : 4800 )	// forcing value to bool 'true' or 'false' (performance warning)
29 	#pragma warning ( disable : 4996 )	// '...' was declared deprecated
30 #endif
31 
32 // =================================================================================================
33 // Support Routines
34 // =================================================================================================
35 
36 
37 #ifndef TraceIterators
38 	#define TraceIterators 0
39 #endif
40 
41 #if TraceIterators
42 	static const char * sStageNames[] = { "before", "self", "qualifiers", "children" };
43 #endif
44 
45 
GetNameSpace(const AdobeXMPCommon::spcIUTF8String & nameSpace)46 XMP_VarString GetNameSpace( const AdobeXMPCommon::spcIUTF8String & nameSpace )
47 {
48 	auto defaultMap = AdobeXMPCore::INameSpacePrefixMap::GetDefaultNameSpacePrefixMap()->GetINameSpacePrefixMap_I();
49 	auto prefix = defaultMap->GetPrefix( nameSpace );
50 	return prefix->c_str();
51 }
52 
53 
NodeFullName(const AdobeXMPCore::spcINode & node)54 XMP_VarString NodeFullName( const AdobeXMPCore::spcINode & node )
55 {
56 	XMP_VarString name = GetNameSpace(node->GetNameSpace()) + ":" + node->GetName()->c_str();
57 	return name;
58 }
59 
AddSchemaProperties(IteratorNode & iterSchema,const char * nameSpace)60 void  XMPIterator2::AddSchemaProperties(IteratorNode & iterSchema, const char * nameSpace)
61 {
62 	using namespace  AdobeXMPCore;
63 
64 	for (auto childIter = mDOM->Iterator(); childIter; childIter = childIter->Next()) {
65 
66 		spINode childNode = childIter->GetNode();
67 		//TODO check name
68 		if (!strcmp(childNode->GetNameSpace()->c_str(), nameSpace)) {
69 			iterSchema.nodeChildren.push_back(IteratorNode( XMPUtils::GetIXMPOptions(childNode), NodeFullName(childNode), 0 ));
70 		}
71 
72 	}
73 
74 }
SetCurrentSchema(XMP_StringPtr schemaName)75  void XMPIterator2::SetCurrentSchema(XMP_StringPtr schemaName)
76 {
77 
78 	info.currSchema = schemaName;
79 
80 }	// SetCurrSchema
81 
SetCurrentSchema(XMP_VarString & schemaName)82  void XMPIterator2::SetCurrentSchema(XMP_VarString & schemaName)
83 {
84 
85 	info.currSchema = schemaName;
86 
87 }	// SetCurrSchema
88 
AdvanceIteratorPosition()89 void XMPIterator2::AdvanceIteratorPosition()
90 {
91 
92 	while (true) {
93 
94 		if (info.currPos == info.endPos) {
95 
96 			if (info.ancestors.empty()) break;
97 
98 			IteratorPosPair & parent = info.ancestors.back();
99 			info.currPos = parent.first;
100 			info.endPos = parent.second;
101 			info.ancestors.pop_back();
102 		}
103 		else {
104 
105 			if (info.currPos->visitStage == kIter_BeforeVisit) {
106 				if (info.currPos->options & kXMP_SchemaNode) SetCurrentSchema(info.currPos->fullPath);
107 				break;
108 			}
109 
110 
111 			if (info.currPos->visitStage == kIter_VisitSelf) {
112 
113 				info.currPos->visitStage = kIter_VisitQualifiers;
114 				if (!info.currPos->nodeQualifiers.empty()) {
115 
116 					info.ancestors.push_back(IteratorPosPair(info.currPos, info.endPos));
117 					info.endPos = info.currPos->nodeQualifiers.end();
118 					info.currPos = info.currPos->nodeQualifiers.begin();
119 					break;
120 				}
121 			}
122 
123 			if (info.currPos->visitStage == kIter_VisitQualifiers) {	// Just finished visiting the qualifiers.
124 				info.currPos->nodeQualifiers.clear();
125 				info.currPos->visitStage = kIter_VisitChildren;			// Start visiting the children.
126 				if (!info.currPos->nodeChildren.empty()) {
127 					info.ancestors.push_back(IteratorPosPair(info.currPos, info.endPos));
128 					info.endPos = info.currPos->nodeChildren.end();		// ! Set the parent's endPos before changing currPos!
129 					info.currPos = info.currPos->nodeChildren.begin();
130 					break;
131 				}
132 			}
133 
134 			if (info.currPos->visitStage == kIter_VisitChildren) {
135 				info.currPos->nodeChildren.clear();
136 				++info.currPos;
137 				continue;
138 			}
139 		}
140 	}
141 
142 	XMP_Assert((info.currPos == info.endPos) || (info.currPos->visitStage == kIter_BeforeVisit));
143 
144 }
145 
GetNextNode(bool & isSchema)146 AdobeXMPCore::spINode XMPIterator2::GetNextNode(bool & isSchema)
147 {
148 	using namespace  AdobeXMPCore;
149 	spINode xmpNode = spINode();
150 
151 	if (info.currPos->visitStage != kIter_BeforeVisit) AdvanceIteratorPosition();
152 
153 	bool isSchemaNode = false;
154 	XMP_ExpandedXPath exPath;
155 
156 	while (info.currPos != info.endPos) {
157 
158 		isSchemaNode = XMP_NodeIsSchema(info.currPos->options);
159 		if (isSchemaNode) {
160 
161 			SetCurrentSchema(info.currPos->fullPath);
162 
163 			isSchema = isSchemaNode;
164 		}
165 		else {
166 
167 			ExpandXPath(info.currSchema.c_str(), info.currPos->fullPath.c_str(), &exPath);
168 			bool found = XMPUtils::FindCnstNode(mDOM, exPath, xmpNode);
169 		}
170 		if (xmpNode ||  isSchemaNode) break;
171 
172 		info.currPos->visitStage = kIter_VisitChildren;
173 		info.currPos->nodeChildren.clear();
174 		info.currPos->nodeQualifiers.clear();
175 
176 		AdvanceIteratorPosition();
177 	}
178 
179 	if (info.currPos == info.endPos) return spINode();
180 
181 	XMP_Assert(info.currPos->visitStage == kIter_BeforeVisit);
182 
183 	if (info.currPos->visitStage == kIter_BeforeVisit) {
184 
185 		if (!isSchemaNode && !(info.options & kXMP_IterJustChildren)) {
186 
187 			AddNodeOffSpring(*info.currPos, xmpNode);
188 		}
189 		info.currPos->visitStage = kIter_VisitSelf;
190 	}
191 
192 	return xmpNode;
193 }
194 
AddNodeOffSpring(IteratorNode & iterParent,const AdobeXMPCore::spINode & xmpParent)195 void XMPIterator2::AddNodeOffSpring(IteratorNode &iterParent, const AdobeXMPCore::spINode & xmpParent)
196 {
197 
198 		using namespace AdobeXMPCore;
199 		XMP_VarString currPath(iterParent.fullPath);
200 		size_t leafOffset = currPath.size();
201 		if (xmpParent->HasQualifiers() && (!(info.options & kXMP_IterOmitQualifiers))) {
202 
203 			currPath += "/?";	// All qualifiers are named and use paths like "Prop/?Qual".
204 			leafOffset += 2;
205 
206 			for ( auto qualIter = xmpParent->QualifiersIterator(); qualIter; qualIter = qualIter->Next() ) {
207 
208 				spINode qualNode = qualIter->GetNode();
209 				//TOTO Add prefix too
210 				currPath += NodeFullName( qualNode );
211 
212 				iterParent.nodeQualifiers.push_back( IteratorNode( XMPUtils::GetIXMPOptions( qualNode ), currPath, leafOffset ) );
213 				currPath.erase( leafOffset );
214 			}
215 
216 			leafOffset -= 2;
217 			currPath.erase(leafOffset);
218 
219 		}
220 		if (XMPUtils::GetNodeChildCount(xmpParent)) {
221 
222 
223 
224 			if (xmpParent->GetNodeType() == INode::kNTStructure) {
225 				currPath += '/';
226 				leafOffset += 1;
227 			}
228 			size_t childIdx = 0;
229 			for (auto childIter = XMPUtils::GetNodeChildIterator(xmpParent); childIter; childIter = childIter->Next(), ++childIdx) {
230 
231 				spcINode xmpChild = childIter->GetNode();
232 				if (xmpParent->GetNodeType() != INode::kNTArray) {
233 					//TODO Add prefix as well
234 					currPath += NodeFullName(xmpChild);
235 				}
236 				else {
237 
238 					char buffer[32];	// AUDIT: Using sizeof(buffer) below for snprintf length is safe.
239 					#if XMP_WinBuild
240 						snprintf(buffer, sizeof(buffer), "[%Iu]", childIdx + 1);	// ! XPath indices are one-based.
241 					#else
242 						snprintf(buffer, sizeof(buffer), "[%zu]", childIdx + 1);
243 					#endif
244 					currPath += buffer;
245 				}
246 
247 				iterParent.nodeChildren.push_back(IteratorNode(XMPUtils::GetIXMPOptions(xmpChild), currPath, leafOffset));
248 				currPath.erase(leafOffset);
249 
250 			}
251 
252 
253 		}
254 
255 
256 
257 	}	// AddNodeOffspring
258 
259 // =================================================================================================
260 // Constructors
261 // =================================================================================================
262 
263 // -------------------------------------------------------------------------------------------------
264 // XMPIterator
265 // -----------
266 //
267 // Constructor for iterations over the nodes in an XMPMeta object. This builds a tree of iteration
268 // nodes that caches the existing node names of the XMPMeta object. The iteration tree is a partial
269 // replica of the XMPMeta tree. The initial iteration tree normally has just the root node, all of
270 // the schema nodes for a full object iteration. Lower level nodes (children and qualifiers) are
271 // added when the parent is visited. If the kXMP_IterJustChildren option is passed then the initial
272 // iterator includes the children and the parent is marked as done. The iteration tree nodes are
273 // pruned when they are no longer needed.
274 
XMPIterator2(const XMPMeta & xmpObjBase,XMP_StringPtr schemaNS,XMP_StringPtr propName,XMP_OptionBits options)275 XMPIterator2::XMPIterator2 ( const XMPMeta & xmpObjBase,
276 						   XMP_StringPtr   schemaNS,
277 						   XMP_StringPtr   propName,
278 						   XMP_OptionBits  options) :XMPIterator(xmpObjBase, schemaNS, propName, options)
279 {
280 	using namespace AdobeXMPCore;
281 	if ( (options & kXMP_IterClassMask) != kXMP_IterProperties ) {
282 		XMP_Throw ( "Unsupported iteration kind", kXMPErr_BadOptions );
283 	}
284 	iteratorOptions = options;
285 	info.options = options;
286 
287 	if(sUseNewCoreAPIs) {
288 
289 		const XMPMeta2 & tempPtr = dynamic_cast<const XMPMeta2 &>(xmpObjBase);
290 
291 	}
292 	else {
293 		XMP_Throw("Unsupported iteration kind", kXMPErr_BadOptions);
294 	}
295 
296 	const XMPMeta2 & xmpObj = dynamic_cast<const XMPMeta2 &>(xmpObjBase);
297 
298 	spIMetadata root = xmpObj.mDOM;
299 	mDOM = xmpObj.mDOM;
300 	// *** Lock the XMPMeta object if we ever stop using a full DLL lock.
301 
302 	if ( *propName != 0 ) {
303 		XMP_ExpandedXPath propPath;
304 		ExpandXPath(schemaNS, propName, &propPath);
305 		spINode destNode;
306 		XMP_OptionBits destOptions = 0;
307 		bool nodeFound = XMPUtils::FindCnstNode(root, propPath, destNode,&destOptions);
308 
309 		if (nodeFound) {
310 
311 			XMP_VarString rootName(propPath[1].step);	// The schema is [0].
312 			for (size_t i = 2; i < propPath.size(); ++i) {
313 				XMP_OptionBits stepKind = GetStepKind(propPath[i].options);
314 				if (stepKind <= kXMP_QualifierStep) rootName += '/';
315 				rootName += propPath[i].step;
316 			}
317 
318 			propName = rootName.c_str();
319 			size_t leafOffset = rootName.size();
320 			while ((leafOffset > 0) && (propName[leafOffset] != '/') && (propName[leafOffset] != '[')) --leafOffset;
321 			if (propName[leafOffset] == '/') ++leafOffset;
322 
323 			info.tree.nodeChildren.push_back( IteratorNode( destOptions, propName, leafOffset ) );
324 			SetCurrentSchema(propPath[kSchemaStep].step.c_str());
325 			if (options & kXMP_IterJustChildren) {
326 
327 				AddNodeOffSpring(info.tree.nodeChildren.back(), destNode);
328 			}
329 
330 		}
331 
332 
333 	} else if ( *schemaNS != 0 ) {
334 
335 		info.tree.nodeChildren.push_back(IteratorNode(kXMP_SchemaNode, schemaNS, 0));
336 		IteratorNode & iterSchema = info.tree.nodeChildren.back();
337 
338 		bool schemaFound = false;
339 
340 		for (auto childIter = mDOM->Iterator(); childIter; childIter = childIter->Next()) {
341 
342 			if (!strcmp(childIter->GetNode()->GetNameSpace()->c_str(), schemaNS)) {
343 
344 				schemaFound = true;
345 				break;
346 			}
347 		}
348 
349 
350 		if (schemaFound) AddSchemaProperties(iterSchema, schemaNS);
351 
352 		if (iterSchema.nodeChildren.empty()) {
353 			info.tree.nodeChildren.pop_back();	// No properties, remove the schema node.
354 		}
355 		else {
356 			SetCurrentSchema(schemaNS);
357 		}
358 
359 
360 	} else {
361 
362 		std::map < XMP_VarString, bool > schemaProperties;
363 
364 		for (auto childIter = mDOM->Iterator(); childIter; childIter = childIter->Next()) {
365 
366 			spINode childNode = childIter->GetNode();
367 
368 			schemaProperties[childNode->GetNameSpace()->c_str()] = true;
369 		}
370 
371 
372 		for (auto key : schemaProperties) {
373 			//TODO check name
374 			info.tree.nodeChildren.push_back(IteratorNode( kXMP_SchemaNode, key.first, 0 ));
375 
376 			IteratorNode & iterSchema = info.tree.nodeChildren.back();
377 
378 			if (!(info.options & kXMP_IterJustChildren)) {
379 				AddSchemaProperties(iterSchema, key.first.c_str());
380 			//	if (iterSchema.nodeChildren.empty()) info.tree.nodeChildren.pop_back();	// No properties, remove the schema node.
381 			}
382 		}
383 
384 
385 
386 
387 	}
388 
389 	info.currPos = info.tree.nodeChildren.begin();
390 	info.endPos = info.tree.nodeChildren.end();
391 
392 	if ((info.options & kXMP_IterJustChildren) && (info.currPos != info.endPos) && (*schemaNS != 0)) {
393 		info.currPos->visitStage = kIter_VisitSelf;
394 	}
395 
396 
397 
398 }	// XMPIterator for XMPMeta objects
399 
400 // -------------------------------------------------------------------------------------------------
401 // XMPIterator
402 // -----------
403 //
404 // Constructor for iterations over global tables such as registered namespaces or aliases.
405 
XMPIterator2(XMP_StringPtr schemaNS,XMP_StringPtr propName,XMP_OptionBits options)406 XMPIterator2::XMPIterator2 ( XMP_StringPtr  schemaNS,
407 	XMP_StringPtr  propName,
408 	XMP_OptionBits options) : XMPIterator(schemaNS, propName, options)
409 {
410 	void * p; p = &schemaNS; p = &propName; p = &options;	// Avoid unused param warnings.
411 	XMP_Throw("Unimplemented XMPIterator constructor for global tables", kXMPErr_Unimplemented);
412 
413 }	// XMPIterator for global tables
414 
415 // -------------------------------------------------------------------------------------------------
416 // ~XMPIterator
417 // -----------
418 
~XMPIterator2()419 XMPIterator2::~XMPIterator2() RELEASE_NO_THROW
420 {
421 
422 	// Let everything else default.
423 
424 }	// ~XMPIterator
425 
426 // =================================================================================================
427 // Iteration Methods
428 // =================================================================================================
429 
430 // -------------------------------------------------------------------------------------------------
431 // Next
432 // ----
433 //
434 // Do a preorder traversal of the cached nodes.
435 
436 // *** Need to document the relationships between currPos, endPos, and visitStage.
437 
438 bool
Next(XMP_StringPtr * schemaNS,XMP_StringLen * nsSize,XMP_StringPtr * propPath,XMP_StringLen * pathSize,XMP_StringPtr * propValue,XMP_StringLen * valueSize,XMP_OptionBits * propOptions)439 XMPIterator2::Next ( XMP_StringPtr * schemaNS,
440 					XMP_StringLen *	 nsSize,
441 					XMP_StringPtr *	 propPath,
442 					XMP_StringLen *	 pathSize,
443 					XMP_StringPtr *	 propValue,
444 					XMP_StringLen *	 valueSize,
445 					XMP_OptionBits * propOptions )
446 {
447 
448 	if (info.currPos == info.endPos) return false;
449 	using namespace AdobeXMPCore;
450 	using namespace AdobeXMPCommon;
451 	bool isSchema = false;
452 	spcINode xmpNode = GetNextNode(isSchema);
453 	if (!xmpNode && !isSchema) return false;
454 	bool isSchemaNode = isSchema;
455 	if (info.options & kXMP_IterJustLeafNodes) {
456 		while (isSchemaNode || ( xmpNode && XMPUtils::GetNodeChildCount(xmpNode)))  {
457 			info.currPos->visitStage = kIter_VisitQualifiers;	// Skip to this node's children.
458 			xmpNode = GetNextNode(isSchemaNode);
459 			if (xmpNode == 0 && !isSchemaNode) return false;
460 			isSchemaNode = XMP_NodeIsSchema(info.currPos->options);
461 		}
462 	}
463 
464 	*schemaNS = info.currSchema.c_str();
465 	*nsSize = info.currSchema.size();
466 
467 
468 	*propOptions = info.currPos->options;
469 
470 	*propPath = "";
471 	*pathSize = 0;
472 	*propValue = "";
473 	*valueSize = 0;
474 
475 	if (!(*propOptions & kXMP_SchemaNode)) {
476 
477 		*propPath = info.currPos->fullPath.c_str();
478 		*pathSize = info.currPos->fullPath.size();
479 
480 		if (info.options & kXMP_IterJustLeafName) {
481 			*propPath += info.currPos->leafOffset;
482 			*pathSize -= info.currPos->leafOffset;
483 			if (! xmpNode->IsArrayItem()) {
484 				*schemaNS = xmpNode->GetNameSpace()->c_str();
485 				*nsSize = xmpNode->GetNameSpace()->size();
486 			}
487 			else {
488 				*schemaNS = "";
489 				*nsSize = 0;
490 			}
491 
492 		}
493 
494 		if (!(*propOptions & kXMP_PropCompositeMask)) {
495 			spcIUTF8String nodeValue = xmpNode->ConvertToSimpleNode()->GetValue();
496 			*propValue = nodeValue->c_str();
497 			*valueSize = nodeValue->size();
498 		}
499 
500 	}
501 
502 	return true;
503 }	// Next
504 
505 // -------------------------------------------------------------------------------------------------
506 // Skip
507 // ----
508 //
509 // Skip some portion of the traversal related to the last visited node. We skip either that node's
510 // children, or those children and the previous node's siblings. The implementation might look a bit
511 // awkward because info.currNode always points to the next node to be visited. We might already have
512 // moved past the things to skip, e.g. if the previous node was simple and the last of its siblings.
513 
514 enum {
515 	kXMP_ValidIterSkipOptions = kXMP_IterSkipSubtree | kXMP_IterSkipSiblings
516 };
517 
518 
519 void
Skip(XMP_OptionBits iterOptions)520 XMPIterator2::Skip ( XMP_OptionBits iterOptions )
521 {
522 	//	if ( (info.currPos == kIter_NullPos) )  XMP_Throw ( "No prior postion to skip from", kXMPErr_BadIterPosition );
523 	if (iterOptions == 0) XMP_Throw("Must specify what to skip", kXMPErr_BadOptions);
524 	if ((iterOptions & ~kXMP_ValidIterSkipOptions) != 0) XMP_Throw("Undefined options", kXMPErr_BadOptions);
525 
526 
527 	if (iterOptions & kXMP_IterSkipSubtree) {
528 
529 		info.currPos->visitStage = kIter_VisitChildren;
530 	}
531 	else if (iterOptions & kXMP_IterSkipSiblings) {
532 
533 		info.currPos = info.endPos;
534 		AdvanceIteratorPosition();
535 	}
536 
537 }	// Skip
538 
539 
540 
541 // =================================================================================================
542 #endif  // ENABLE_CPP_DOM_MODEL