/src/exiv2/xmpsdk/src/XMPIterator.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | // ================================================================================================= |
2 | | // Copyright 2002-2007 Adobe Systems Incorporated |
3 | | // All Rights Reserved. |
4 | | // |
5 | | // NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms |
6 | | // of the Adobe license agreement accompanying it. |
7 | | // ================================================================================================= |
8 | | |
9 | | #include "XMP_Environment.h" // ! This must be the first include! |
10 | | #include "XMPCore_Impl.hpp" |
11 | | |
12 | | #include "XMPIterator.hpp" |
13 | | |
14 | | #include <string> |
15 | | #include <stdio.h> // For snprintf. |
16 | | |
17 | | #if XMP_WinBuild |
18 | | #ifdef _MSC_VER |
19 | | #pragma warning ( disable : 4702 ) // unreachable code |
20 | | #pragma warning ( disable : 4800 ) // forcing value to bool 'true' or 'false' (performance warning) |
21 | | #pragma warning ( disable : 4996 ) // '...' was declared deprecated |
22 | | #endif |
23 | | #endif |
24 | | |
25 | | // ================================================================================================= |
26 | | // Support Routines |
27 | | // ================================================================================================= |
28 | | |
29 | | |
30 | | #ifndef TraceIterators |
31 | | #define TraceIterators 0 |
32 | | #endif |
33 | | |
34 | | #if TraceIterators |
35 | | static const char * sStageNames[] = { "before", "self", "qualifiers", "children" }; |
36 | | #endif |
37 | | |
38 | | static XMP_Node * sDummySchema = 0; // ! Used for some ugliness with aliases. |
39 | | |
40 | | // ------------------------------------------------------------------------------------------------- |
41 | | // AddSchemaProps |
42 | | // -------------- |
43 | | // |
44 | | // Add the top level properties to the IterNode for a schema. |
45 | | |
46 | | static void |
47 | | AddSchemaProps ( IterInfo & info, IterNode & iterSchema, const XMP_Node * xmpSchema ) |
48 | 0 | { |
49 | 0 | UNUSED(info); |
50 | | #if TraceIterators |
51 | | printf ( " Adding properties of %s\n", xmpSchema->name.c_str() ); |
52 | | #endif |
53 | |
|
54 | 0 | for ( size_t propNum = 0, propLim = xmpSchema->children.size(); propNum != propLim; ++propNum ) { |
55 | 0 | const XMP_Node * xmpProp = xmpSchema->children[propNum]; |
56 | | // *** set the has-aliases bit when appropriate |
57 | 0 | iterSchema.children.push_back ( IterNode ( xmpProp->options, xmpProp->name, 0 ) ); |
58 | | #if TraceIterators |
59 | | printf ( " %s\n", xmpProp->name.c_str() ); |
60 | | #endif |
61 | 0 | } |
62 | |
|
63 | 0 | } // AddSchemaProps |
64 | | |
65 | | // ------------------------------------------------------------------------------------------------- |
66 | | // AddSchemaAliases |
67 | | // ---------------- |
68 | | // |
69 | | // Add the aliases to the IterNode for a schema, if the corresponding actual exists. |
70 | | |
71 | | static void |
72 | | AddSchemaAliases ( IterInfo & info, IterNode & iterSchema, XMP_StringPtr schemaURI ) |
73 | 0 | { |
74 | | |
75 | | // We're showing the aliases also. Look them up by their namespace prefix. Yes, the alias map is |
76 | | // sorted so we could process just that portion. But that takes more code and the extra speed |
77 | | // isn't worth it. (Plus this way we avoid a dependence on the map implementation.) Lookup the |
78 | | // XMP node from the alias, to make sure the actual exists. |
79 | | |
80 | | #if TraceIterators |
81 | | printf ( " Adding aliases\n", schemaURI ); |
82 | | #endif |
83 | |
|
84 | 0 | XMP_StringPtr nsPrefix; |
85 | 0 | XMP_StringLen nsLen; |
86 | 0 | bool found = XMPMeta::GetNamespacePrefix ( schemaURI, &nsPrefix, &nsLen ); |
87 | 0 | if ( ! found ) XMP_Throw ( "Unknown iteration namespace", kXMPErr_BadSchema ); |
88 | | |
89 | 0 | XMP_AliasMapPos currAlias = sRegisteredAliasMap->begin(); |
90 | 0 | XMP_AliasMapPos endAlias = sRegisteredAliasMap->end(); |
91 | | |
92 | 0 | for ( ; currAlias != endAlias; ++currAlias ) { |
93 | 0 | if ( XMP_LitNMatch ( currAlias->first.c_str(), nsPrefix, nsLen ) ) { |
94 | 0 | const XMP_Node * actualProp = FindConstNode ( &info.xmpObj->tree, currAlias->second ); |
95 | 0 | if ( actualProp != 0 ) { |
96 | 0 | iterSchema.children.push_back ( IterNode ( (actualProp->options | kXMP_PropIsAlias), currAlias->first, 0 ) ); |
97 | | #if TraceIterators |
98 | | printf ( " %s => %s\n", currAlias->first.c_str(), actualProp->name.c_str() ); |
99 | | #endif |
100 | 0 | } |
101 | 0 | } |
102 | 0 | } |
103 | |
|
104 | 0 | } // AddSchemaAliases |
105 | | |
106 | | // ------------------------------------------------------------------------------------------------- |
107 | | // AddNodeOffspring |
108 | | // ---------------- |
109 | | // |
110 | | // Add the immediate children and qualifiers to an IterNode. |
111 | | |
112 | | static void |
113 | | AddNodeOffspring ( IterInfo & info, IterNode & iterParent, const XMP_Node * xmpParent ) |
114 | 0 | { |
115 | 0 | XMP_VarString currPath ( iterParent.fullPath ); |
116 | 0 | size_t leafOffset = iterParent.fullPath.size(); |
117 | | |
118 | 0 | if ( (! xmpParent->qualifiers.empty()) && (! (info.options & kXMP_IterOmitQualifiers)) ) { |
119 | |
|
120 | | #if TraceIterators |
121 | | printf ( " Adding qualifiers of %s\n", currPath.c_str() ); |
122 | | #endif |
123 | |
|
124 | 0 | currPath += "/?"; // All qualifiers are named and use paths like "Prop/?Qual". |
125 | 0 | leafOffset += 2; |
126 | | |
127 | 0 | for ( size_t qualNum = 0, qualLim = xmpParent->qualifiers.size(); qualNum != qualLim; ++qualNum ) { |
128 | 0 | const XMP_Node * xmpQual = xmpParent->qualifiers[qualNum]; |
129 | 0 | currPath += xmpQual->name; |
130 | 0 | iterParent.qualifiers.push_back ( IterNode ( xmpQual->options, currPath, leafOffset ) ); |
131 | 0 | currPath.erase ( leafOffset ); |
132 | | #if TraceIterators |
133 | | printf ( " %s\n", xmpQual->name.c_str() ); |
134 | | #endif |
135 | 0 | } |
136 | | |
137 | 0 | leafOffset -= 2; |
138 | 0 | currPath.erase ( leafOffset ); |
139 | |
|
140 | 0 | } |
141 | |
|
142 | 0 | if ( ! xmpParent->children.empty() ) { |
143 | | |
144 | | #if TraceIterators |
145 | | printf ( " Adding children of %s\n", currPath.c_str() ); |
146 | | #endif |
147 | |
|
148 | 0 | XMP_Assert ( xmpParent->options & kXMP_PropCompositeMask ); |
149 | | |
150 | 0 | if ( xmpParent->options & kXMP_PropValueIsStruct ) { |
151 | 0 | currPath += '/'; |
152 | 0 | leafOffset += 1; |
153 | 0 | } |
154 | | |
155 | 0 | for ( size_t childNum = 0, childLim = xmpParent->children.size(); childNum != childLim; ++childNum ) { |
156 | 0 | const XMP_Node * xmpChild = xmpParent->children[childNum]; |
157 | 0 | if ( ! (xmpParent->options & kXMP_PropValueIsArray) ) { |
158 | 0 | currPath += xmpChild->name; |
159 | 0 | } else { |
160 | 0 | char buffer [32]; // AUDIT: Using sizeof(buffer) below for snprintf length is safe. |
161 | 0 | snprintf ( buffer, sizeof(buffer), "[%lu]", static_cast<unsigned long>(childNum+1) ); // ! XPath indices are one-based. |
162 | 0 | currPath += buffer; |
163 | 0 | } |
164 | 0 | iterParent.children.push_back ( IterNode ( xmpChild->options, currPath, leafOffset ) ); |
165 | 0 | currPath.erase ( leafOffset ); |
166 | | #if TraceIterators |
167 | | printf ( " %s\n", (iterParent.children.back().fullPath.c_str() + leafOffset) ); |
168 | | #endif |
169 | 0 | } |
170 | | |
171 | 0 | } |
172 | |
|
173 | 0 | } // AddNodeOffspring |
174 | | |
175 | | // ------------------------------------------------------------------------------------------------- |
176 | | // SetCurrSchema |
177 | | // ------------- |
178 | | |
179 | | static inline void |
180 | | SetCurrSchema ( IterInfo & info, XMP_StringPtr schemaName ) |
181 | 0 | { |
182 | |
|
183 | 0 | info.currSchema = schemaName; |
184 | | #if 0 // *** XMP_DebugBuild |
185 | | info._schemaPtr = info.currSchema.c_str(); |
186 | | #endif |
187 | |
|
188 | 0 | } // SetCurrSchema |
189 | | |
190 | | static inline void |
191 | | SetCurrSchema ( IterInfo & info, XMP_VarString & schemaName ) |
192 | 0 | { |
193 | |
|
194 | 0 | info.currSchema = schemaName; |
195 | | #if 0 // *** XMP_DebugBuild |
196 | | info._schemaPtr = info.currSchema.c_str(); |
197 | | #endif |
198 | |
|
199 | 0 | } // SetCurrSchema |
200 | | |
201 | | // ------------------------------------------------------------------------------------------------- |
202 | | // AdvanceIterPos |
203 | | // -------------- |
204 | | // |
205 | | // Adjust currPos and possibly endPos for the next step in a pre-order depth-first traversal. The |
206 | | // current node has just been visited, move on to its qualifiers, children, then siblings, or back |
207 | | // up to an ancestor. AdvanceIterPos either moves to a property or qualifier node that can be |
208 | | // visited, or to the end of the entire iteration. |
209 | | |
210 | | static void |
211 | | AdvanceIterPos ( IterInfo & info ) |
212 | 0 | { |
213 | | // ------------------------------------------------------------------------------------------- |
214 | | // Keep looking until we find a node to visit or the end of everything. The first time through |
215 | | // the current node will exist, we just visited it. But we have to keep looking if the current |
216 | | // node was the last of its siblings or is an empty schema. |
217 | | |
218 | | // ! It is possible that info.currPos == info.endPos on entry. Don't dereference info.currPos yet! |
219 | |
|
220 | 0 | while ( true ) { |
221 | | |
222 | 0 | if ( info.currPos == info.endPos ) { |
223 | | |
224 | | // ------------------------------------------------------------------------------------ |
225 | | // At the end of a set of siblings, move up to an ancestor. We've either just finished |
226 | | // the qualifiers and will move to the children, or have just finished the children and |
227 | | // will move on to the next sibling. |
228 | | |
229 | 0 | if ( info.ancestors.empty() ) break; // We're at the end of the schema list. |
230 | | |
231 | 0 | IterPosPair & parent = info.ancestors.back(); |
232 | 0 | info.currPos = parent.first; |
233 | 0 | info.endPos = parent.second; |
234 | 0 | info.ancestors.pop_back(); |
235 | | |
236 | | #if TraceIterators |
237 | | printf ( " Moved up to %s, stage = %s\n", |
238 | | info.currPos->fullPath.c_str(), sStageNames[info.currPos->visitStage] ); |
239 | | #endif |
240 | | |
241 | 0 | } else { |
242 | | |
243 | | // ------------------------------------------------------------------------------------------- |
244 | | // Decide what to do with this iteration node based on its state. Don't use a switch statement, |
245 | | // some of the cases want to break from the loop. A break in a switch just exits the case. |
246 | | |
247 | | #if TraceIterators |
248 | | printf ( " Moving from %s, stage = %s\n", |
249 | | info.currPos->fullPath.c_str(), sStageNames[info.currPos->visitStage] ); |
250 | | #endif |
251 | | |
252 | 0 | if ( info.currPos->visitStage == kIter_BeforeVisit ) { // Visit this node now. |
253 | 0 | if ( info.currPos->options & kXMP_SchemaNode ) SetCurrSchema ( info, info.currPos->fullPath ); |
254 | 0 | break; |
255 | 0 | } |
256 | | |
257 | 0 | if ( info.currPos->visitStage == kIter_VisitSelf ) { // Just finished visiting the value portion. |
258 | 0 | info.currPos->visitStage = kIter_VisitQualifiers; // Start visiting the qualifiers. |
259 | 0 | if ( ! info.currPos->qualifiers.empty() ) { |
260 | 0 | info.ancestors.push_back ( IterPosPair ( info.currPos, info.endPos ) ); |
261 | 0 | info.endPos = info.currPos->qualifiers.end(); // ! Set the parent's endPos before changing currPos! |
262 | 0 | info.currPos = info.currPos->qualifiers.begin(); |
263 | 0 | break; |
264 | 0 | } |
265 | 0 | } |
266 | | |
267 | 0 | if ( info.currPos->visitStage == kIter_VisitQualifiers ) { // Just finished visiting the qualifiers. |
268 | 0 | info.currPos->qualifiers.clear(); |
269 | 0 | info.currPos->visitStage = kIter_VisitChildren; // Start visiting the children. |
270 | 0 | if ( ! info.currPos->children.empty() ) { |
271 | 0 | info.ancestors.push_back ( IterPosPair ( info.currPos, info.endPos ) ); |
272 | 0 | info.endPos = info.currPos->children.end(); // ! Set the parent's endPos before changing currPos! |
273 | 0 | info.currPos = info.currPos->children.begin(); |
274 | 0 | break; |
275 | 0 | } |
276 | 0 | } |
277 | | |
278 | 0 | if ( info.currPos->visitStage == kIter_VisitChildren ) { // Just finished visiting the children. |
279 | 0 | info.currPos->children.clear(); |
280 | 0 | ++info.currPos; // Move to the next sibling. |
281 | 0 | continue; |
282 | 0 | } |
283 | | |
284 | | #if TraceIterators |
285 | | if ( info.currPos != info.endPos ) { |
286 | | printf ( " Moved to %s, stage = %s\n", |
287 | | info.currPos->fullPath.c_str(), sStageNames[info.currPos->visitStage] ); |
288 | | } |
289 | | #endif |
290 | | |
291 | 0 | } |
292 | |
|
293 | 0 | } // Loop to find the next node. |
294 | | |
295 | 0 | XMP_Assert ( (info.currPos == info.endPos) || (info.currPos->visitStage == kIter_BeforeVisit) ); |
296 | |
|
297 | 0 | } // AdvanceIterPos |
298 | | |
299 | | // ------------------------------------------------------------------------------------------------- |
300 | | // GetNextXMPNode |
301 | | // -------------- |
302 | | // |
303 | | // Used by XMPIterator::Next to obtain the next XMP node, ignoring the kXMP_IterJustLeafNodes flag. |
304 | | // This isolates some messy code, allowing a clean loop in Next if kXMP_IterJustLeafNodes is set. |
305 | | |
306 | | static const XMP_Node * |
307 | | GetNextXMPNode ( IterInfo & info ) |
308 | 0 | { |
309 | 0 | const XMP_Node * xmpNode = 0; |
310 | | |
311 | | // ---------------------------------------------------------------------------------------------- |
312 | | // On entry currPos points to an iteration node whose state is either before-visit or visit-self. |
313 | | // If it is before-visit then we will return that node's value part now. If it is visit-self it |
314 | | // means the previous iteration returned the value portion of that node, so we can advance to the |
315 | | // next node in the iteration tree. Then we find the corresponding XMP node, allowing for the XMP |
316 | | // tree to have been modified since that part of the iteration tree was constructed. |
317 | | |
318 | | // ! NOTE: Supporting aliases throws in some nastiness with schemas. There might not be any XMP |
319 | | // ! node for the schema, but we still have to visit it because of possible aliases. The static |
320 | | // ! sDummySchema is returned if there is no real schema node. |
321 | |
|
322 | 0 | if ( info.currPos->visitStage != kIter_BeforeVisit ) AdvanceIterPos ( info ); |
323 | | |
324 | 0 | bool isSchemaNode = false; |
325 | 0 | XMP_ExpandedXPath expPath; // Keep outside the loop to avoid constant construct/destruct. |
326 | | |
327 | 0 | while ( info.currPos != info.endPos ) { |
328 | |
|
329 | 0 | isSchemaNode = XMP_NodeIsSchema ( info.currPos->options ); |
330 | 0 | if ( isSchemaNode ) { |
331 | 0 | SetCurrSchema ( info, info.currPos->fullPath ); |
332 | 0 | xmpNode = FindConstSchema ( &info.xmpObj->tree, info.currPos->fullPath.c_str() ); |
333 | 0 | if ( xmpNode == 0 ) xmpNode = sDummySchema; |
334 | 0 | } else { |
335 | 0 | ExpandXPath ( info.currSchema.c_str(), info.currPos->fullPath.c_str(), &expPath ); |
336 | 0 | xmpNode = FindConstNode ( &info.xmpObj->tree, expPath ); |
337 | 0 | } |
338 | 0 | if ( xmpNode != 0 ) break; // Exit the loop, we found a live XMP node. |
339 | | |
340 | 0 | info.currPos->visitStage = kIter_VisitChildren; // Make AdvanceIterPos move to the next sibling. |
341 | 0 | info.currPos->children.clear(); |
342 | 0 | info.currPos->qualifiers.clear(); |
343 | 0 | AdvanceIterPos ( info ); |
344 | |
|
345 | 0 | } |
346 | |
|
347 | 0 | if ( info.currPos == info.endPos ) return 0; |
348 | | |
349 | | // ------------------------------------------------------------------------------------------- |
350 | | // Now we've got the iteration node and corresponding XMP node. Add the iteration children for |
351 | | // structs and arrays. The children of schema were added when the iterator was constructed. |
352 | | |
353 | 0 | XMP_Assert ( info.currPos->visitStage == kIter_BeforeVisit ); |
354 | |
|
355 | 0 | if ( info.currPos->visitStage == kIter_BeforeVisit ) { |
356 | 0 | if ( (! isSchemaNode) && (! (info.options & kXMP_IterJustChildren)) ) { |
357 | 0 | AddNodeOffspring ( info, *info.currPos, xmpNode ); |
358 | 0 | } |
359 | 0 | info.currPos->visitStage = kIter_VisitSelf; |
360 | 0 | } |
361 | | |
362 | 0 | return xmpNode; |
363 | |
|
364 | 0 | } // GetNextXMPNode |
365 | | |
366 | | // ================================================================================================= |
367 | | // Init/Term |
368 | | // ================================================================================================= |
369 | | |
370 | | // ------------------------------------------------------------------------------------------------- |
371 | | // Initialize |
372 | | // ---------- |
373 | | |
374 | | /* class static */ bool |
375 | | XMPIterator::Initialize() |
376 | 1 | { |
377 | 1 | sDummySchema = new XMP_Node ( 0, "dummy:schema/", kXMP_SchemaNode); |
378 | 1 | return true; |
379 | | |
380 | 1 | } // Initialize |
381 | | |
382 | | // ------------------------------------------------------------------------------------------------- |
383 | | // Terminate |
384 | | // ---------- |
385 | | |
386 | | /* class static */ void |
387 | | XMPIterator::Terminate() RELEASE_NO_THROW |
388 | 18.5k | { |
389 | 18.5k | delete ( sDummySchema ); |
390 | 18.5k | sDummySchema = 0; |
391 | 18.5k | return; |
392 | | |
393 | 18.5k | } // Terminate |
394 | | |
395 | | // ------------------------------------------------------------------------------------------------- |
396 | | // Unlock |
397 | | // ------ |
398 | | |
399 | | void |
400 | | XMPIterator::Unlock ( XMP_OptionBits options ) |
401 | 0 | { |
402 | 0 | UNUSED(options); |
403 | |
|
404 | 0 | XMPMeta::Unlock ( 0 ); |
405 | | |
406 | 0 | } // Unlock |
407 | | |
408 | | // ================================================================================================= |
409 | | // Constructors |
410 | | // ================================================================================================= |
411 | | |
412 | | // ------------------------------------------------------------------------------------------------- |
413 | | // XMPIterator |
414 | | // ----------- |
415 | | // |
416 | | // Constructor for iterations over the nodes in an XMPMeta object. This builds a tree of iteration |
417 | | // nodes that caches the existing node names of the XMPMeta object. The iteration tree is a partial |
418 | | // replica of the XMPMeta tree. The initial iteration tree normally has just the root node, all of |
419 | | // the schema nodes for a full object iteration. Lower level nodes (children and qualifiers) are |
420 | | // added when the parent is visited. If the kXMP_IterJustChildren option is passed then the initial |
421 | | // iterator includes the children and the parent is marked as done. The iteration tree nodes are |
422 | | // pruned when they are no longer needed. |
423 | | |
424 | | XMPIterator::XMPIterator ( const XMPMeta & xmpObj, |
425 | | XMP_StringPtr schemaNS, |
426 | | XMP_StringPtr propName, |
427 | 0 | XMP_OptionBits options ) : clientRefs(0), info(IterInfo(options,&xmpObj)) |
428 | 0 | { |
429 | 0 | if ( (options & kXMP_IterClassMask) != kXMP_IterProperties ) { |
430 | 0 | XMP_Throw ( "Unsupported iteration kind", kXMPErr_BadOptions ); |
431 | 0 | } |
432 | | |
433 | | // *** Lock the XMPMeta object if we ever stop using a full DLL lock. |
434 | | |
435 | 0 | if ( *propName != 0 ) { |
436 | | |
437 | | // An iterator rooted at a specific node. |
438 | |
|
439 | | #if TraceIterators |
440 | | printf ( "\nNew XMP property iterator for \"%s\", options = %X\n Schema = %s, root = %s\n", |
441 | | xmpObj.tree.name.c_str(), options, schemaNS, propName ); |
442 | | #endif |
443 | | |
444 | 0 | XMP_ExpandedXPath propPath; |
445 | 0 | ExpandXPath ( schemaNS, propName, &propPath ); |
446 | 0 | XMP_Node * propNode = FindConstNode ( &xmpObj.tree, propPath ); // If not found get empty iteration. |
447 | | |
448 | 0 | if ( propNode != 0 ) { |
449 | |
|
450 | 0 | XMP_VarString rootName ( propPath[1].step ); // The schema is [0]. |
451 | 0 | for ( size_t i = 2; i < propPath.size(); ++i ) { |
452 | 0 | XMP_OptionBits stepKind = GetStepKind ( propPath[i].options ); |
453 | 0 | if ( stepKind <= kXMP_QualifierStep ) rootName += '/'; |
454 | 0 | rootName += propPath[i].step; |
455 | 0 | } |
456 | |
|
457 | 0 | propName = rootName.c_str(); |
458 | 0 | size_t leafOffset = rootName.size(); |
459 | 0 | while ( (leafOffset > 0) && (propName[leafOffset] != '/') && (propName[leafOffset] != '[') ) --leafOffset; |
460 | 0 | if ( propName[leafOffset] == '/' ) ++leafOffset; |
461 | |
|
462 | 0 | info.tree.children.push_back ( IterNode ( propNode->options, propName, leafOffset ) ); |
463 | 0 | SetCurrSchema ( info, propPath[kSchemaStep].step.c_str() ); |
464 | 0 | if ( info.options & kXMP_IterJustChildren ) { |
465 | 0 | AddNodeOffspring ( info, info.tree.children.back(), propNode ); |
466 | 0 | } |
467 | |
|
468 | 0 | } |
469 | | |
470 | 0 | } else if ( *schemaNS != 0 ) { |
471 | | |
472 | | // An iterator for all properties in one schema. |
473 | | |
474 | | #if TraceIterators |
475 | | printf ( "\nNew XMP schema iterator for \"%s\", options = %X\n Schema = %s\n", |
476 | | xmpObj.tree.name.c_str(), options, schemaNS ); |
477 | | #endif |
478 | | |
479 | 0 | info.tree.children.push_back ( IterNode ( kXMP_SchemaNode, schemaNS, 0 ) ); |
480 | 0 | IterNode & iterSchema = info.tree.children.back(); |
481 | | |
482 | 0 | XMP_Node * xmpSchema = FindConstSchema ( &xmpObj.tree, schemaNS ); |
483 | 0 | if ( xmpSchema != 0 ) AddSchemaProps ( info, iterSchema, xmpSchema ); |
484 | | |
485 | 0 | if ( info.options & kXMP_IterIncludeAliases ) AddSchemaAliases ( info, iterSchema, schemaNS ); |
486 | | |
487 | 0 | if ( iterSchema.children.empty() ) { |
488 | 0 | info.tree.children.pop_back(); // No properties, remove the schema node. |
489 | 0 | } else { |
490 | 0 | SetCurrSchema ( info, schemaNS ); |
491 | 0 | } |
492 | | |
493 | 0 | } else { |
494 | | |
495 | | // An iterator for all properties in all schema. First add schema that exist (have children), |
496 | | // adding aliases from them if appropriate. Then add schema that have no actual properties |
497 | | // but do have aliases to existing properties, if we're including aliases in the iteration. |
498 | | |
499 | | #if TraceIterators |
500 | | printf ( "\nNew XMP tree iterator for \"%s\", options = %X\n", |
501 | | xmpObj.tree.name.c_str(), options ); |
502 | | #endif |
503 | | |
504 | | // First pick up the schema that exist. |
505 | | |
506 | 0 | for ( size_t schemaNum = 0, schemaLim = xmpObj.tree.children.size(); schemaNum != schemaLim; ++schemaNum ) { |
507 | |
|
508 | 0 | const XMP_Node * xmpSchema = xmpObj.tree.children[schemaNum]; |
509 | 0 | info.tree.children.push_back ( IterNode ( kXMP_SchemaNode, xmpSchema->name, 0 ) ); |
510 | 0 | IterNode & iterSchema = info.tree.children.back(); |
511 | |
|
512 | 0 | if ( ! (info.options & kXMP_IterJustChildren) ) { |
513 | 0 | AddSchemaProps ( info, iterSchema, xmpSchema ); |
514 | 0 | if ( info.options & kXMP_IterIncludeAliases ) AddSchemaAliases ( info, iterSchema, xmpSchema->name.c_str() ); |
515 | 0 | if ( iterSchema.children.empty() ) info.tree.children.pop_back(); // No properties, remove the schema node. |
516 | 0 | } |
517 | |
|
518 | 0 | } |
519 | | |
520 | 0 | if ( info.options & kXMP_IterIncludeAliases ) { |
521 | | |
522 | | // Add the schema that only have aliases. The most convenient, and safest way, is to go |
523 | | // through the registered namespaces, see if it exists, and let AddSchemaAliases do its |
524 | | // thing if not. Don't combine with the above loop, it is nicer to have the "real" stuff |
525 | | // be in storage order (not subject to the namespace map order). |
526 | | |
527 | | // ! We don't do the kXMP_IterJustChildren handing in the same way here as above. The |
528 | | // ! existing schema (presumably) have actual children. We need to call AddSchemaAliases |
529 | | // ! here to determine if the namespace has any aliases to existing properties. We then |
530 | | // ! strip the children if necessary. |
531 | |
|
532 | 0 | XMP_cStringMapPos currNS = sNamespaceURIToPrefixMap->begin(); |
533 | 0 | XMP_cStringMapPos endNS = sNamespaceURIToPrefixMap->end(); |
534 | 0 | for ( ; currNS != endNS; ++currNS ) { |
535 | 0 | XMP_StringPtr schemaName = currNS->first.c_str(); |
536 | 0 | if ( FindConstSchema ( &xmpObj.tree, schemaName ) != 0 ) continue; |
537 | 0 | info.tree.children.push_back ( IterNode ( kXMP_SchemaNode, schemaName, 0 ) ); |
538 | 0 | IterNode & iterSchema = info.tree.children.back(); |
539 | 0 | AddSchemaAliases ( info, iterSchema, schemaName ); |
540 | 0 | if ( iterSchema.children.empty() ) { |
541 | 0 | info.tree.children.pop_back(); // No aliases, remove the schema node. |
542 | 0 | } else if ( info.options & kXMP_IterJustChildren ) { |
543 | 0 | iterSchema.children.clear(); // Get rid of the children. |
544 | 0 | } |
545 | 0 | } |
546 | |
|
547 | 0 | } |
548 | |
|
549 | 0 | } |
550 | | |
551 | | // Set the current iteration position to the first node to be visited. |
552 | | |
553 | 0 | info.currPos = info.tree.children.begin(); |
554 | 0 | info.endPos = info.tree.children.end(); |
555 | | |
556 | 0 | if ( (info.options & kXMP_IterJustChildren) && (info.currPos != info.endPos) && (*schemaNS != 0) ) { |
557 | 0 | info.currPos->visitStage = kIter_VisitSelf; |
558 | 0 | } |
559 | |
|
560 | | #if TraceIterators |
561 | | if ( info.currPos == info.endPos ) { |
562 | | printf ( " ** Empty iteration **\n" ); |
563 | | } else { |
564 | | printf ( " Initial node %s, stage = %s, iterator @ %.8X\n", |
565 | | info.currPos->fullPath.c_str(), sStageNames[info.currPos->visitStage], this ); |
566 | | } |
567 | | #endif |
568 | | |
569 | 0 | } // XMPIterator for XMPMeta objects |
570 | | |
571 | | // ------------------------------------------------------------------------------------------------- |
572 | | // XMPIterator |
573 | | // ----------- |
574 | | // |
575 | | // Constructor for iterations over global tables such as registered namespaces or aliases. |
576 | | |
577 | | XMPIterator::XMPIterator ( XMP_StringPtr /*schemaNS*/, |
578 | | XMP_StringPtr /*propName*/, |
579 | 0 | XMP_OptionBits options ) : clientRefs(0), info(IterInfo(options,0)) |
580 | 0 | { |
581 | |
|
582 | 0 | XMP_Throw ( "Unimplemented XMPIterator constructor for global tables", kXMPErr_Unimplemented ); |
583 | |
|
584 | 0 | } // XMPIterator for global tables |
585 | | |
586 | | // ------------------------------------------------------------------------------------------------- |
587 | | // ~XMPIterator |
588 | | // ----------- |
589 | | |
590 | | XMPIterator::~XMPIterator() RELEASE_NO_THROW |
591 | 0 | { |
592 | 0 | XMP_Assert ( this->clientRefs <= 0 ); |
593 | | // Let everything else default. |
594 | | |
595 | 0 | } // ~XMPIterator |
596 | | |
597 | | // ================================================================================================= |
598 | | // Iteration Methods |
599 | | // ================================================================================================= |
600 | | |
601 | | // ------------------------------------------------------------------------------------------------- |
602 | | // Next |
603 | | // ---- |
604 | | // |
605 | | // Do a preorder traversal of the cached nodes. |
606 | | |
607 | | // *** Need to document the relationships between currPos, endPos, and visitStage. |
608 | | |
609 | | bool |
610 | | XMPIterator::Next ( XMP_StringPtr * schemaNS, |
611 | | XMP_StringLen * nsSize, |
612 | | XMP_StringPtr * propPath, |
613 | | XMP_StringLen * pathSize, |
614 | | XMP_StringPtr * propValue, |
615 | | XMP_StringLen * valueSize, |
616 | | XMP_OptionBits * propOptions ) |
617 | 0 | { |
618 | | // *** Lock the XMPMeta object if we ever stop using a full DLL lock. |
619 | | |
620 | | // ! NOTE: Supporting aliases throws in some nastiness with schemas. There might not be any XMP |
621 | | // ! node for the schema, but we still have to visit it because of possible aliases. |
622 | | |
623 | 0 | if ( info.currPos == info.endPos ) return false; // Happens at the start of an empty iteration. |
624 | | |
625 | | #if TraceIterators |
626 | | printf ( "Next iteration from %s, stage = %s, iterator @ %.8X\n", |
627 | | info.currPos->fullPath.c_str(), sStageNames[info.currPos->visitStage], this ); |
628 | | #endif |
629 | | |
630 | 0 | const XMP_Node * xmpNode = GetNextXMPNode ( info ); |
631 | 0 | if ( xmpNode == 0 ) return false; |
632 | 0 | bool isSchemaNode = XMP_NodeIsSchema ( info.currPos->options ); |
633 | | |
634 | 0 | if ( info.options & kXMP_IterJustLeafNodes ) { |
635 | 0 | while ( isSchemaNode || (! xmpNode->children.empty()) ) { |
636 | 0 | info.currPos->visitStage = kIter_VisitQualifiers; // Skip to this node's children. |
637 | 0 | xmpNode = GetNextXMPNode ( info ); |
638 | 0 | if ( xmpNode == 0 ) return false; |
639 | 0 | isSchemaNode = XMP_NodeIsSchema ( info.currPos->options ); |
640 | 0 | } |
641 | 0 | } |
642 | | |
643 | 0 | *schemaNS = info.currSchema.c_str(); |
644 | 0 | *nsSize = info.currSchema.size(); |
645 | |
|
646 | 0 | *propOptions = info.currPos->options; |
647 | |
|
648 | 0 | *propPath = ""; |
649 | 0 | *pathSize = 0; |
650 | 0 | *propValue = ""; |
651 | 0 | *valueSize = 0; |
652 | | |
653 | 0 | if ( ! (*propOptions & kXMP_SchemaNode) ) { |
654 | |
|
655 | 0 | *propPath = info.currPos->fullPath.c_str(); |
656 | 0 | *pathSize = info.currPos->fullPath.size(); |
657 | 0 | if ( info.options & kXMP_IterJustLeafName ) { |
658 | 0 | *propPath += info.currPos->leafOffset; |
659 | 0 | *pathSize -= info.currPos->leafOffset; |
660 | 0 | } |
661 | | |
662 | 0 | if ( ! (*propOptions & kXMP_PropCompositeMask) ) { |
663 | 0 | *propValue = xmpNode->value.c_str(); |
664 | 0 | *valueSize = xmpNode->value.size(); |
665 | 0 | } |
666 | |
|
667 | 0 | } |
668 | | |
669 | | #if TraceIterators |
670 | | printf ( " Next node %s, stage = %s\n", |
671 | | info.currPos->fullPath.c_str(), sStageNames[info.currPos->visitStage] ); |
672 | | #endif |
673 | | |
674 | 0 | return true; |
675 | |
|
676 | 0 | } // Next |
677 | | |
678 | | // ------------------------------------------------------------------------------------------------- |
679 | | // Skip |
680 | | // ---- |
681 | | // |
682 | | // Skip some portion of the traversal related to the last visited node. We skip either that node's |
683 | | // children, or those children and the previous node's siblings. The implementation might look a bit |
684 | | // awkward because info.currNode always points to the next node to be visited. We might already have |
685 | | // moved past the things to skip, e.g. if the previous node was simple and the last of its siblings. |
686 | | |
687 | | enum { |
688 | | kXMP_ValidIterSkipOptions = kXMP_IterSkipSubtree | kXMP_IterSkipSiblings |
689 | | }; |
690 | | |
691 | | void |
692 | | XMPIterator::Skip ( XMP_OptionBits iterOptions ) |
693 | 0 | { |
694 | | // if ( (info.currPos == kIter_NullPos) ) XMP_Throw ( "No prior postion to skip from", kXMPErr_BadIterPosition ); |
695 | 0 | if ( iterOptions == 0 ) XMP_Throw ( "Must specify what to skip", kXMPErr_BadOptions ); |
696 | 0 | if ( (iterOptions & ~kXMP_ValidIterSkipOptions) != 0 ) XMP_Throw ( "Undefined options", kXMPErr_BadOptions ); |
697 | |
|
698 | | #if TraceIterators |
699 | | printf ( "Skipping from %s, stage = %s, iterator @ %.8X", |
700 | | info.currPos->fullPath.c_str(), sStageNames[info.currPos->visitStage], this ); |
701 | | #endif |
702 | | |
703 | 0 | if ( iterOptions & kXMP_IterSkipSubtree ) { |
704 | | #if TraceIterators |
705 | | printf ( ", mode = subtree\n" ); |
706 | | #endif |
707 | 0 | info.currPos->visitStage = kIter_VisitChildren; |
708 | 0 | } else if ( iterOptions & kXMP_IterSkipSiblings ) { |
709 | | #if TraceIterators |
710 | | printf ( ", mode = siblings\n" ); |
711 | | #endif |
712 | 0 | info.currPos = info.endPos; |
713 | 0 | AdvanceIterPos ( info ); |
714 | 0 | } |
715 | | #if TraceIterators |
716 | | printf ( " Skipped to %s, stage = %s\n", |
717 | | info.currPos->fullPath.c_str(), sStageNames[info.currPos->visitStage] ); |
718 | | #endif |
719 | | |
720 | |
|
721 | 0 | } // Skip |
722 | | |
723 | | // ------------------------------------------------------------------------------------------------- |
724 | | // UnlockIter |
725 | | // ---------- |
726 | | |
727 | | void |
728 | | XMPIterator::UnlockIter ( XMP_OptionBits options ) |
729 | 0 | { |
730 | 0 | UNUSED(options); |
731 | |
|
732 | 0 | XMPMeta::Unlock ( 0 ); |
733 | | |
734 | 0 | } // UnlockIter |
735 | | |
736 | | // ================================================================================================= |