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 "XMP_Version.h"
11 #include "XMPCore_Impl.hpp"
12 #include "XMPMeta.hpp"	// *** For use of GetNamespacePrefix in FindSchemaNode.
13 
14 #include "UnicodeInlines.incl_cpp"
15 
16 #include <algorithm>
17 
18 using namespace std;
19 
20 #if XMP_WinBuild
21     #ifdef _MSC_VER
22         #pragma warning ( disable : 4290 )	// C++ exception specification ignored except ... not __declspec(nothrow)
23         #pragma warning ( disable : 4800 )	// forcing value to bool 'true' or 'false' (performance warning)
24     #endif
25 #endif
26 
27 // *** Add debug codegen checks, e.g. that typical masking operations really work
28 // *** Make option constants 0x...UL.
29 
30 // Internal code should be using #if with XMP_MacBuild, XMP_WinBuild, or XMP_UNIXBuild.
31 // This is a sanity check in case of accidental use of *_ENV. Some clients use the poor
32 // practice of defining the *_ENV macro with an empty value.
33 #if defined ( MAC_ENV )
34 	#if ! MAC_ENV
35 		#error "MAC_ENV must be defined so that \"#if MAC_ENV\" is true"
36 	#endif
37 #elif defined ( WIN_ENV )
38 	#if ! WIN_ENV
39 		#error "WIN_ENV must be defined so that \"#if WIN_ENV\" is true"
40 	#endif
41 #elif defined ( UNIX_ENV )
42 	#if ! UNIX_ENV
43 		#error "UNIX_ENV must be defined so that \"#if UNIX_ENV\" is true"
44 	#endif
45 #endif
46 
47 // =================================================================================================
48 // Static Variables
49 // ================
50 
51 XMP_Int32 sXMP_InitCount = 0;
52 
53 XMP_StringMap *	sNamespaceURIToPrefixMap = 0;
54 XMP_StringMap *	sNamespacePrefixToURIMap = 0;
55 
56 XMP_AliasMap *	sRegisteredAliasMap = 0;	// Needed by XMPIterator.
57 
58 XMP_VarString *	sOutputNS  = 0;
59 XMP_VarString *	sOutputStr = 0;
60 XMP_VarString * sExceptionMessage = 0;
61 
62 XMP_Mutex sXMPCoreLock;
63 int sLockCount = 0;
64 
65 #if TraceXMPCalls
66 	FILE * xmpOut = stderr;
67 #endif
68 
69 void *              voidVoidPtr    = 0;	// Used to backfill null output parameters.
70 XMP_StringPtr		voidStringPtr  = 0;
71 XMP_StringLen		voidStringLen  = 0;
72 XMP_OptionBits		voidOptionBits = 0;
73 XMP_Uns8			voidByte       = 0;
74 bool				voidBool       = 0;
75 XMP_Int32			voidInt32      = 0;
76 XMP_Int64			voidInt64      = 0;
77 double				voidDouble     = 0.0;
78 XMP_DateTime		voidDateTime;
79 WXMP_Result 		void_wResult;
80 
81 // =================================================================================================
82 // Mutex Utilities
83 // ===============
84 
85 // ! Note that the mutex need not be "recursive", allowing the same thread to acquire it multiple
86 // ! times. There is a single XMP lock which is acquired in the wrapper classes. Internal calls
87 // ! never go back out to the wrappers.
88 
89 #if XMP_WinBuild
90 
XMP_InitMutex(XMP_Mutex * mutex)91 	bool XMP_InitMutex ( XMP_Mutex * mutex ) {
92 		InitializeCriticalSection ( mutex );
93 		return true;
94 	}
95 
XMP_TermMutex(XMP_Mutex & mutex)96 	void XMP_TermMutex ( XMP_Mutex & mutex ) {
97 		DeleteCriticalSection ( &mutex );
98 	}
99 
XMP_EnterCriticalRegion(XMP_Mutex & mutex)100 	void XMP_EnterCriticalRegion ( XMP_Mutex & mutex ) {
101 		EnterCriticalSection ( &mutex );
102 	}
103 
XMP_ExitCriticalRegion(XMP_Mutex & mutex)104 	void XMP_ExitCriticalRegion ( XMP_Mutex & mutex ) {
105 		LeaveCriticalSection ( &mutex );
106 	}
107 
108 #else
109 
110 	// Use pthread for both Mac and generic UNIX.
111 	// ! Would be nice to specify PTHREAD_MUTEX_ERRORCHECK, but the POSIX documentation is useless.
112 	// ! Would be OK but overkill to specify PTHREAD_MUTEX_RECURSIVE.
113 
XMP_InitMutex(XMP_Mutex * mutex)114 	bool XMP_InitMutex ( XMP_Mutex * mutex ) {
115 		int err = pthread_mutex_init ( mutex, 0 );
116 		return (err == 0 );
117 	}
118 
XMP_TermMutex(XMP_Mutex & mutex)119 	void XMP_TermMutex ( XMP_Mutex & mutex ) {
120 		(void) pthread_mutex_destroy ( &mutex );
121 	}
122 
XMP_EnterCriticalRegion(XMP_Mutex & mutex)123 	void XMP_EnterCriticalRegion ( XMP_Mutex & mutex ) {
124 		int err = pthread_mutex_lock ( &mutex );
125 		if ( err != 0 ) XMP_Throw ( "XMP_EnterCriticalRegion - pthread_mutex_lock failure", kXMPErr_ExternalFailure );
126 	}
127 
XMP_ExitCriticalRegion(XMP_Mutex & mutex)128 	void XMP_ExitCriticalRegion ( XMP_Mutex & mutex ) {
129 		int err = pthread_mutex_unlock ( &mutex );
130 		if ( err != 0 ) XMP_Throw ( "XMP_ExitCriticalRegion - pthread_mutex_unlock failure", kXMPErr_ExternalFailure );
131 	}
132 
133 #endif
134 
135 // =================================================================================================
136 // Local Utilities
137 // ===============
138 
139 // -------------------------------------------------------------------------------------------------
140 // VerifyXPathRoot
141 // ---------------
142 //
143 // Set up the first 2 components of the expanded XPath. Normalizes the various cases of using the
144 // full schema URI and/or a qualified root property name. Returns true for normal processing. If
145 // allowUnknownSchemaNS is true and the schema namespace is not registered, false is returned. If
146 // allowUnknownSchemaNS is false and the schema namespace is not registered, an exception is thrown.
147 
148 // *** Should someday check the full syntax.
149 
150 static void
VerifyXPathRoot(XMP_StringPtr schemaURI,XMP_StringPtr propName,XMP_ExpandedXPath * expandedXPath)151 VerifyXPathRoot	( XMP_StringPtr			schemaURI,
152 				  XMP_StringPtr			propName,
153 				  XMP_ExpandedXPath *	expandedXPath )
154 {
155 	// Do some basic checks on the URI and name. Try to lookup the URI. See if the name is qualified.
156 
157 	XMP_Assert ( (schemaURI != 0) && (propName != 0) && (*propName != 0) );
158 	XMP_Assert ( (expandedXPath != 0) && (expandedXPath->empty()) );
159 
160 	if ( *schemaURI == 0 ) XMP_Throw ( "Schema namespace URI is required", kXMPErr_BadSchema );
161 
162 	if ( (*propName == '?') || (*propName == '@') ) {
163 		XMP_Throw ( "Top level name must not be a qualifier", kXMPErr_BadXPath );
164 	}
165 	for ( XMP_StringPtr ch = propName; *ch != 0; ++ch ) {
166 		if ( (*ch == '/') || (*ch == '[') ) {
167 			XMP_Throw ( "Top level name must be simple", kXMPErr_BadXPath );
168 		}
169 	}
170 
171 	XMP_StringMapPos uriPos = sNamespaceURIToPrefixMap->find ( XMP_VarString ( schemaURI ) );
172 	if ( uriPos == sNamespaceURIToPrefixMap->end() ) {
173 		XMP_Throw ( "Unregistered schema namespace URI", kXMPErr_BadSchema );
174 	}
175 
176 	XMP_StringPtr colonPos = propName;
177 	while ( (*colonPos != 0) && (*colonPos != ':') ) ++colonPos;
178 	VerifySimpleXMLName ( propName, colonPos );	// Verify the part before any colon.
179 
180 	// Verify the various URI and prefix combinations. Initialize the expanded XPath.
181 
182 	if ( *colonPos == 0 ) {
183 
184 		// The propName is unqualified, use the schemaURI and associated prefix.
185 
186 		expandedXPath->push_back ( XPathStepInfo ( schemaURI, kXMP_SchemaNode ) );
187 		expandedXPath->push_back ( XPathStepInfo ( uriPos->second, 0 ) );
188 		(*expandedXPath)[kRootPropStep].step += propName;
189 
190 	} else {
191 
192 		// The propName is qualified. Make sure the prefix is legit. Use the associated URI and qualified name.
193 
194 		size_t prefixLen = colonPos - propName + 1;	// ! Include the colon.
195 		VerifySimpleXMLName ( colonPos+1, colonPos+strlen(colonPos) );
196 
197 		XMP_VarString prefix ( propName, prefixLen );
198 		XMP_StringMapPos prefixPos = sNamespacePrefixToURIMap->find ( prefix );
199 		if ( prefixPos == sNamespacePrefixToURIMap->end() ) {
200 			XMP_Throw ( "Unknown schema namespace prefix", kXMPErr_BadSchema );
201 		}
202 		if ( prefix != uriPos->second ) {
203 			XMP_Throw ( "Schema namespace URI and prefix mismatch", kXMPErr_BadSchema );
204 		}
205 
206 		expandedXPath->push_back ( XPathStepInfo ( schemaURI, kXMP_SchemaNode ) );
207 		expandedXPath->push_back ( XPathStepInfo ( propName, 0 ) );
208 
209 	}
210 
211 }	// VerifyXPathRoot
212 
213 // -------------------------------------------------------------------------------------------------
214 // VerifyQualName
215 // --------------
216 
217 static void
VerifyQualName(XMP_StringPtr qualName,XMP_StringPtr nameEnd)218 VerifyQualName ( XMP_StringPtr qualName, XMP_StringPtr nameEnd )
219 {
220 	if ( qualName >= nameEnd ) XMP_Throw ( "Empty qualified name", kXMPErr_BadXPath );
221 
222 	XMP_StringPtr colonPos = qualName;
223 	while ( (colonPos < nameEnd) && (*colonPos != ':') ) ++colonPos;
224 	if ( (colonPos == qualName) || (colonPos >= nameEnd) ) XMP_Throw ( "Ill-formed qualified name", kXMPErr_BadXPath );
225 
226 	VerifySimpleXMLName ( qualName, colonPos );
227 	VerifySimpleXMLName ( colonPos+1, nameEnd );
228 
229 	size_t prefixLen = colonPos - qualName + 1;	// ! Include the colon.
230 	XMP_VarString prefix ( qualName, prefixLen );
231 	XMP_StringMapPos prefixPos = sNamespacePrefixToURIMap->find ( prefix );
232 	if ( prefixPos == sNamespacePrefixToURIMap->end() ) {
233 		XMP_Throw ( "Unknown namespace prefix for qualified name", kXMPErr_BadXPath );
234 	}
235 
236 }	// VerifyQualName
237 
238 // -------------------------------------------------------------------------------------------------
239 // FindIndexedItem
240 // ---------------
241 //
242 //	[index]	An element of an array.
243 //
244 // Support the implicit creation of a new last item.
245 
246 static XMP_Index
FindIndexedItem(XMP_Node * arrayNode,const XMP_VarString & indexStep,bool createNodes)247 FindIndexedItem ( XMP_Node * arrayNode, const XMP_VarString & indexStep, bool createNodes )
248 {
249 	XMP_Index index = 0;
250 	size_t    chLim = indexStep.size() - 1;
251 
252 	XMP_Assert ( (chLim >= 2) && (indexStep[0] == '[') && (indexStep[chLim] == ']') );
253 
254 	for ( size_t chNum = 1; chNum != chLim; ++chNum ) {
255 		XMP_Assert ( ('0' <= indexStep[chNum]) && (indexStep[chNum] <= '9') );
256 		index = (index * 10) + (indexStep[chNum] - '0');
257 		if ( index < 0 ) {
258 			XMP_Throw ( "Array index overflow", kXMPErr_BadXPath );	// ! Overflow, not truly negative.
259 		}
260 	}
261 
262 	--index;	// Change to a C-style, zero based index.
263 	if ( index < 0 ) XMP_Throw ( "Array index must be larger than zero", kXMPErr_BadXPath );
264 
265 	if ( (index == (XMP_Index)arrayNode->children.size()) && createNodes ) {	// Append a new last+1 node.
266 		XMP_Node * newItem = new XMP_Node ( arrayNode, kXMP_ArrayItemName, kXMP_NewImplicitNode );
267 		arrayNode->children.push_back ( newItem );
268 	}
269 
270 	// ! Don't throw here for a too large index. SetProperty will throw, GetProperty will not.
271 	if ( index >= (XMP_Index)arrayNode->children.size() ) index = -1;
272 	return index;
273 
274 }	// FindIndexedItem
275 
276 // -------------------------------------------------------------------------------------------------
277 // SplitNameAndValue
278 // -----------------
279 //
280 // Split the name and value parts for field and qualifier selectors:
281 //
282 //	[qualName="value"]	An element in an array of structs, chosen by a field value.
283 //	[?qualName="value"]	An element in an array, chosen by a qualifier value.
284 //
285 // The value portion is a string quoted by ''' or '"'. The value may contain any character including
286 // a doubled quoting character. The value may be empty.
287 
288 static void
SplitNameAndValue(const XMP_VarString & selStep,XMP_VarString * nameStr,XMP_VarString * valueStr)289 SplitNameAndValue ( const XMP_VarString & selStep, XMP_VarString * nameStr, XMP_VarString * valueStr )
290 {
291 	XMP_StringPtr partBegin = selStep.c_str();
292 	XMP_StringPtr partEnd;
293 
294 	const XMP_StringPtr valueEnd = partBegin + (selStep.size() - 2);
295 	const char          quote    = *valueEnd;
296 
297 	XMP_Assert ( (*partBegin == '[') && (*(valueEnd+1) == ']') );
298 	XMP_Assert ( (selStep.size() >= 6) && ((quote == '"') || (quote == '\'')) );
299 
300 	// Extract the name part.
301 
302 	++partBegin;	// Skip the opening '['.
303 	if ( *partBegin == '?' ) ++partBegin;
304 	for ( partEnd = partBegin+1; *partEnd != '='; ++partEnd ) {};
305 
306 	nameStr->assign ( partBegin, (partEnd - partBegin) );
307 
308 	// Extract the value part, reducing doubled quotes.
309 
310 	XMP_Assert ( *(partEnd+1) == quote );
311 
312 	partBegin = partEnd + 2;
313 	valueStr->erase();
314 	valueStr->reserve ( valueEnd - partBegin );	// Maximum length, don't optimize doubled quotes.
315 
316 	for ( partEnd = partBegin; partEnd < valueEnd; ++partEnd ) {
317 		if ( (*partEnd == quote) && (*(partEnd+1) == quote) ) {
318 			++partEnd;
319 			valueStr->append ( partBegin, (partEnd - partBegin) );
320 			partBegin = partEnd+1;	// ! Loop will increment partEnd again.
321 		}
322 	}
323 
324 	valueStr->append ( partBegin, (partEnd - partBegin) );	// ! The loop does not add the last part.
325 
326 }	// SplitNameAndValue
327 
328 // -------------------------------------------------------------------------------------------------
329 // LookupQualSelector
330 // ------------------
331 //
332 //	[?qualName="value"]	An element in an array, chosen by a qualifier value.
333 //
334 // Note that we don't create implicit nodes for qualifier selectors, so no CreateNodes parameter.
335 
336 static XMP_Index
LookupQualSelector(XMP_Node * arrayNode,const XMP_VarString & qualName,XMP_VarString & qualValue)337 LookupQualSelector ( XMP_Node * arrayNode, const XMP_VarString & qualName, XMP_VarString & qualValue )
338 {
339 	XMP_Index index;
340 
341 	if ( qualName == "xml:lang" ) {
342 
343 		// *** Should check that the value is legit RFC 1766/3066.
344 		NormalizeLangValue ( &qualValue );
345 		index = LookupLangItem ( arrayNode, qualValue ) ;
346 
347 	} else {
348 
349 		XMP_Index itemLim;
350 		for ( index = 0, itemLim = arrayNode->children.size(); index != itemLim; ++index ) {
351 
352 			const XMP_Node * currItem = arrayNode->children[index];
353 			XMP_Assert ( currItem->parent == arrayNode );
354 
355 			size_t q, qualLim;
356 			for ( q = 0, qualLim = currItem->qualifiers.size(); q != qualLim; ++q ) {
357 				const XMP_Node * currQual = currItem->qualifiers[q];
358 				XMP_Assert ( currQual->parent == currItem );
359 				if ( currQual->name != qualName ) continue;
360 				if ( currQual->value == qualValue ) break;	// Exit qual loop.
361 			}
362 			if ( q != qualLim ) break;	// Exit child loop, found an item with a matching qualifier.
363 
364 		}
365 		if ( index == itemLim ) index = -1;
366 
367 	}
368 
369 	return index;
370 
371 }	// LookupQualSelector
372 
373 // -------------------------------------------------------------------------------------------------
374 // FollowXPathStep
375 // ---------------
376 //
377 // After processing by ExpandXPath, a step can be of these forms:
378 //	qualName			A top level property or struct field.
379 //	[index]				An element of an array.
380 //	[last()]			The last element of an array.
381 //	[qualName="value"]	An element in an array of structs, chosen by a field value.
382 //	[?qualName="value"]	An element in an array, chosen by a qualifier value.
383 //	?qualName			A general qualifier.
384 //
385 // Find the appropriate child node, resolving aliases, and optionally creating nodes.
386 
387 static XMP_Node *
FollowXPathStep(XMP_Node * parentNode,const XMP_ExpandedXPath & fullPath,size_t stepNum,bool createNodes,XMP_NodePtrPos * ptrPos,bool aliasedArrayItem=false)388 FollowXPathStep	( XMP_Node *	   parentNode,
389 				  const XMP_ExpandedXPath & fullPath,
390 				  size_t		   stepNum,
391 				  bool			   createNodes,
392 				  XMP_NodePtrPos * ptrPos,
393 				  bool			   aliasedArrayItem = false )
394 {
395 	XMP_Node * nextNode = 0;
396 	const XPathStepInfo & nextStep = fullPath[stepNum];
397 	XMP_Index      index    = 0;
398 	XMP_OptionBits stepKind = nextStep.options & kXMP_StepKindMask;
399 
400 	XMP_Assert ( (kXMP_StructFieldStep <= stepKind) && (stepKind <= kXMP_FieldSelectorStep) );
401 
402 	if ( stepKind == kXMP_StructFieldStep ) {
403 
404 		nextNode = FindChildNode ( parentNode, nextStep.step.c_str(), createNodes, ptrPos );
405 
406 	} else if ( stepKind == kXMP_QualifierStep ) {
407 
408 		XMP_StringPtr qualStep = nextStep.step.c_str();
409 		XMP_Assert ( *qualStep == '?' );
410 		++qualStep;
411 		nextNode = FindQualifierNode ( parentNode, qualStep, createNodes, ptrPos );
412 
413 	} else {
414 
415 		// This is an array indexing step. First get the index, then get the node.
416 
417 		if ( ! (parentNode->options & kXMP_PropValueIsArray) ) {
418 			XMP_Throw ( "Indexing applied to non-array", kXMPErr_BadXPath );
419 		}
420 
421 		if ( stepKind == kXMP_ArrayIndexStep ) {
422 			index = FindIndexedItem ( parentNode, nextStep.step, createNodes );
423 		} else if ( stepKind == kXMP_ArrayLastStep ) {
424 			index = parentNode->children.size() - 1;
425 		} else if ( stepKind == kXMP_FieldSelectorStep ) {
426 			XMP_VarString fieldName, fieldValue;
427 			SplitNameAndValue ( nextStep.step, &fieldName, &fieldValue );
428 			index = LookupFieldSelector ( parentNode, fieldName.c_str(), fieldValue.c_str() );
429 		} else if ( stepKind == kXMP_QualSelectorStep ) {
430 			XMP_VarString qualName, qualValue;
431 			SplitNameAndValue ( nextStep.step, &qualName, &qualValue );
432 			index = LookupQualSelector ( parentNode, qualName, qualValue );
433 		} else {
434 			XMP_Throw ( "Unknown array indexing step in FollowXPathStep", kXMPErr_InternalFailure );
435 		}
436 
437 		if ( (0 <= index) && (index <= (XMP_Index)parentNode->children.size()) ) nextNode = parentNode->children[index];
438 
439 		if ( (index == -1) && createNodes && aliasedArrayItem && (stepKind == kXMP_QualSelectorStep) ) {
440 
441 			// An ugly special case without an obvious better place to be. We have an alias to the
442 			// x-default item of an alt-text array. A simple reference via SetProperty must create
443 			// the x-default item if it does not yet exist.
444 
445 			XMP_Assert ( parentNode->options & kXMP_PropArrayIsAltText );
446 			XMP_Assert ( (stepNum == 2) && (nextStep.step == "[?xml:lang=\"x-default\"]") );
447 
448 			nextNode = new XMP_Node ( parentNode, kXMP_ArrayItemName,
449 									  (kXMP_PropHasQualifiers | kXMP_PropHasLang | kXMP_NewImplicitNode) );
450 
451 			XMP_Node * langQual = new XMP_Node ( nextNode, "xml:lang", "x-default", kXMP_PropIsQualifier );
452 			nextNode->qualifiers.push_back ( langQual );
453 
454 			if ( parentNode->children.empty() ) {
455 				parentNode->children.push_back ( nextNode );
456 			} else {
457 				parentNode->children.insert ( parentNode->children.begin(), nextNode );
458 			}
459 
460 			index = 0;	// ! C-style index! The x-default item is always first.
461 
462 		}
463 
464 		if ( (nextNode != 0) && (ptrPos != 0) ) *ptrPos = parentNode->children.begin() + index;
465 
466 	}
467 
468 	if ( (nextNode != 0) && (nextNode->options & kXMP_NewImplicitNode) ) {
469 		nextNode->options |= (nextStep.options & kXMP_PropArrayFormMask);
470 	}
471 
472 	XMP_Assert ( (ptrPos == 0) || (nextNode == 0) || (nextNode == **ptrPos) );
473 	XMP_Assert ( (nextNode != 0) || (! createNodes) );
474 	return nextNode;
475 
476 }	// FollowXPathStep
477 
478 // -------------------------------------------------------------------------------------------------
479 // CheckImplicitStruct
480 // -------------------
481 
482 static inline void
CheckImplicitStruct(XMP_Node * node,const XMP_ExpandedXPath & expandedXPath,size_t stepNum,size_t stepLim)483 CheckImplicitStruct	( XMP_Node * node,
484 					  const XMP_ExpandedXPath &	expandedXPath,
485 					  size_t	 stepNum,
486 					  size_t	 stepLim )
487 {
488 
489 	if ( (stepNum < stepLim) &&
490 		 ((node->options & kXMP_PropCompositeMask) == 0) &&
491 		 (GetStepKind ( expandedXPath[stepNum].options ) == kXMP_StructFieldStep) ) {
492 
493 		node->options |= kXMP_PropValueIsStruct;
494 
495 	}
496 
497 }	// CheckImplicitStruct
498 
499 // -------------------------------------------------------------------------------------------------
500 // DeleteSubtree
501 // -------------
502 
503 // *** Might be useful elsewhere?
504 
505 static void
DeleteSubtree(XMP_NodePtrPos rootNodePos)506 DeleteSubtree ( XMP_NodePtrPos rootNodePos )
507 {
508 	XMP_Node * rootNode   = *rootNodePos;
509 	XMP_Node * rootParent = rootNode->parent;
510 
511 	if ( ! (rootNode->options & kXMP_PropIsQualifier) ) {
512 
513 		rootParent->children.erase ( rootNodePos );
514 
515 	} else {
516 
517 		rootParent->qualifiers.erase ( rootNodePos );
518 
519 		XMP_Assert ( rootParent->options & kXMP_PropHasQualifiers);
520 		if ( rootParent->qualifiers.empty() ) rootParent->options ^= kXMP_PropHasQualifiers;
521 
522 		if ( rootNode->name == "xml:lang" ) {
523 			XMP_Assert ( rootParent->options & kXMP_PropHasLang);
524 			rootParent->options ^= kXMP_PropHasLang;
525 		} else if ( rootNode->name == "rdf:type" ) {
526 			XMP_Assert ( rootParent->options & kXMP_PropHasType);
527 			rootParent->options ^= kXMP_PropHasType;
528 		}
529 
530 	}
531 
532 	delete rootNode;
533 
534 }	// DeleteSubtree
535 
536 // =================================================================================================
537 // =================================================================================================
538 
539 // =================================================================================================
540 // VerifySetOptions
541 // ================
542 //
543 // Normalize and verify the option flags for SetProperty and similar functions. The allowed options
544 // here are just those that apply to the property, that would be kept in the XMP_Node. Others that
545 // affect the selection of the node or other processing must be removed by now. These are:
546 //	kXMP_InsertBeforeItem
547 //	kXMP_InsertAfterItem
548 //	kXMP_KeepQualifiers
549 //	kXMPUtil_AllowCommas
550 
551 enum {
552 	kXMP_AllSetOptionsMask	= (kXMP_PropValueIsURI       |
553 							   kXMP_PropValueIsStruct    |
554 							   kXMP_PropValueIsArray     |
555 							   kXMP_PropArrayIsOrdered   |
556 							   kXMP_PropArrayIsAlternate |
557 							   kXMP_PropArrayIsAltText   |
558 							   kXMP_DeleteExisting)
559 };
560 
561 XMP_OptionBits
VerifySetOptions(XMP_OptionBits options,XMP_StringPtr propValue)562 VerifySetOptions ( XMP_OptionBits options, XMP_StringPtr propValue )
563 {
564 
565 	if ( options & kXMP_PropArrayIsAltText )   options |= kXMP_PropArrayIsAlternate;
566 	if ( options & kXMP_PropArrayIsAlternate ) options |= kXMP_PropArrayIsOrdered;
567 	if ( options & kXMP_PropArrayIsOrdered )   options |= kXMP_PropValueIsArray;
568 
569 	if ( options & ~kXMP_AllSetOptionsMask ) {
570 		XMP_Throw ( "Unrecognized option flags", kXMPErr_BadOptions );
571 	}
572 
573 	if ( (options & kXMP_PropValueIsStruct) && (options & kXMP_PropValueIsArray) ) {
574 		XMP_Throw ( "IsStruct and IsArray options are mutually exclusive", kXMPErr_BadOptions );
575 	}
576 
577 	if ( (options & kXMP_PropValueOptionsMask) && (options & kXMP_PropCompositeMask) ) {
578 		XMP_Throw ( "Structs and arrays can't have \"value\" options", kXMPErr_BadOptions );
579 	}
580 
581 	if ( (propValue != 0) && (options & kXMP_PropCompositeMask) ) {
582 		XMP_Throw ( "Structs and arrays can't have string values", kXMPErr_BadOptions );
583 	}
584 
585 	return options;
586 
587 }	// VerifySetOptions
588 
589 // =================================================================================================
590 // ComposeXPath
591 // ============
592 //
593 // Compose the canonical string form of an expanded XPath expression.
594 
595 extern void
ComposeXPath(const XMP_ExpandedXPath & expandedXPath,XMP_VarString * stringXPath)596 ComposeXPath ( const XMP_ExpandedXPath & expandedXPath,
597 			   XMP_VarString * stringXPath )
598 {
599 	*stringXPath = expandedXPath[kRootPropStep].step;
600 
601 	for ( size_t index = kRootPropStep+1; index < expandedXPath.size(); ++index ) {
602 		const XPathStepInfo & currStep = expandedXPath[index];
603 
604 		switch ( currStep.options & kXMP_StepKindMask ) {
605 
606 			case kXMP_StructFieldStep :
607 			case kXMP_QualifierStep :
608 				*stringXPath += '/';
609 				*stringXPath += currStep.step;
610 				break;
611 
612 			case kXMP_ArrayIndexStep :
613 			case kXMP_ArrayLastStep :
614 			case kXMP_QualSelectorStep :
615 			case kXMP_FieldSelectorStep :
616 				*stringXPath += currStep.step;
617 				break;
618 
619 			default:
620 				XMP_Throw ( "Unexpected", kXMPErr_InternalFailure );
621 
622 		}
623 
624 	}
625 
626 }	// ComposeXPath
627 
628 // =================================================================================================
629 // ExpandXPath
630 // ===========
631 //
632 // Split an XPath expression apart at the conceptual steps, adding the root namespace prefix to the
633 // first property component. The schema URI is put in the first (0th) slot in the expanded XPath.
634 // Check if the top level component is an alias, but don't resolve it.
635 //
636 // In the most verbose case steps are separated by '/', and each step can be of these forms:
637 //
638 //	qualName				A top level property or struct field.
639 //	*[index]				An element of an array.
640 //	*[last()]				The last element of an array.
641 //	*[fieldName="value"]	An element in an array of structs, chosen by a field value.
642 //	*[@xml:lang="value"]	An element in an alt-text array, chosen by the xml:lang qualifier.
643 //	*[?qualName="value"]	An element in an array, chosen by a qualifier value.
644 //	@xml:lang				An xml:lang qualifier.
645 //	?qualName				A general qualifier.
646 //
647 // The logic is complicated though by shorthand for arrays, the separating '/' and leading '*'
648 // are optional. These are all equivalent:  array/*[2]  array/[2]  array*[2]  array[2]
649 // All of these are broken into the 2 steps "array" and "[2]".
650 //
651 // The value portion in the array selector forms is a string quoted by ''' or '"'. The value
652 // may contain any character including a doubled quoting character. The value may be empty.
653 //
654 // The syntax isn't checked, but an XML name begins with a letter or '_', and contains letters,
655 // digits, '.', '-', '_', and a bunch of special non-ASCII Unicode characters. An XML qualified
656 // name is a pair of names separated by a colon.
657 
658 void
ExpandXPath(XMP_StringPtr schemaNS,XMP_StringPtr propPath,XMP_ExpandedXPath * expandedXPath)659 ExpandXPath	( XMP_StringPtr			schemaNS,
660 			  XMP_StringPtr			propPath,
661 			  XMP_ExpandedXPath *	expandedXPath )
662 {
663 	XMP_Assert ( (schemaNS != 0) && (propPath != 0) && (*propPath != 0) && (expandedXPath != 0) );
664 
665 	XMP_StringPtr	stepBegin, stepEnd;
666 	XMP_StringPtr	qualName, nameEnd;
667 	XMP_VarString	currStep;
668 
669 	qualName = nameEnd = NULL;
670 	size_t resCount = 2;	// Guess at the number of steps. At least 2, plus 1 for each '/' or '['.
671 	for ( stepEnd = propPath; *stepEnd != 0; ++stepEnd ) {
672 		if ( (*stepEnd == '/') || (*stepEnd == '[') ) ++resCount;
673 	}
674 
675 	expandedXPath->clear();
676 	expandedXPath->reserve ( resCount );
677 
678 	// -------------------------------------------------------------------------------------------
679 	// Pull out the first component and do some special processing on it: add the schema namespace
680 	// prefix and see if it is an alias. The start must be a qualName.
681 
682 	stepBegin = propPath;
683 	stepEnd = stepBegin;
684 	while ( (*stepEnd != 0) && (*stepEnd != '/') && (*stepEnd != '[') && (*stepEnd != '*') ) ++stepEnd;
685 	if ( stepEnd == stepBegin ) XMP_Throw ( "Empty initial XPath step", kXMPErr_BadXPath );
686 	currStep.assign ( stepBegin, (stepEnd - stepBegin) );
687 
688 	VerifyXPathRoot ( schemaNS, currStep.c_str(), expandedXPath );
689 
690 	XMP_OptionBits stepFlags = kXMP_StructFieldStep;
691 	if ( sRegisteredAliasMap->find ( (*expandedXPath)[kRootPropStep].step ) != sRegisteredAliasMap->end() ) {
692 		stepFlags |= kXMP_StepIsAlias;
693 	}
694 	(*expandedXPath)[kRootPropStep].options |= stepFlags;
695 
696 	// -----------------------------------------------------
697 	// Now continue to process the rest of the XPath string.
698 
699 	while ( *stepEnd != 0 ) {
700 
701 		stepBegin = stepEnd;
702 		if ( *stepBegin == '/' ) ++stepBegin;
703 		if ( *stepBegin == '*' ) {
704 			++stepBegin;
705 			if ( *stepBegin != '[' ) XMP_Throw ( "Missing '[' after '*'", kXMPErr_BadXPath );
706 		}
707 		stepEnd = stepBegin;
708 
709 		if ( *stepBegin != '[' ) {
710 
711 			// A struct field or qualifier.
712 			qualName = stepBegin;
713 			while ( (*stepEnd != 0) && (*stepEnd != '/') && (*stepEnd != '[') && (*stepEnd != '*') ) ++stepEnd;
714 			nameEnd = stepEnd;
715 			stepFlags = kXMP_StructFieldStep;	// ! Touch up later, also changing '@' to '?'.
716 
717 		} else {
718 
719 			// One of the array forms.
720 
721 			++stepEnd;	// Look at the character after the leading '['.
722 
723 			if ( ('0' <= *stepEnd) && (*stepEnd <= '9') ) {
724 
725 				// A numeric (decimal integer) array index.
726 				while ( (*stepEnd != 0) && ('0' <= *stepEnd) && (*stepEnd <= '9') ) ++stepEnd;
727 				if ( *stepEnd != ']' ) XMP_Throw ( "Missing ']' for integer array index", kXMPErr_BadXPath );
728 				stepFlags = kXMP_ArrayIndexStep;
729 
730 			} else {
731 
732 				// Could be "[last()]" or one of the selector forms. Find the ']' or '='.
733 
734 				while ( (*stepEnd != 0) && (*stepEnd != ']') && (*stepEnd != '=') ) ++stepEnd;
735 				if ( *stepEnd == 0 ) XMP_Throw ( "Missing ']' or '=' for array index", kXMPErr_BadXPath );
736 
737 				if ( *stepEnd == ']' ) {
738 
739 					if ( strncmp ( "[last()", stepBegin, (stepEnd - stepBegin) ) != 0 ) {
740 						XMP_Throw ( "Invalid non-numeric array index", kXMPErr_BadXPath );
741 					}
742 					stepFlags = kXMP_ArrayLastStep;
743 
744 				} else {
745 
746 					qualName = stepBegin+1;
747 					nameEnd = stepEnd;
748 					++stepEnd;	// Absorb the '=', remember the quote.
749 					const char quote = *stepEnd;
750 					if ( (quote != '\'') && (quote != '"') ) {
751 						XMP_Throw ( "Invalid quote in array selector", kXMPErr_BadXPath );
752 					}
753 
754 					++stepEnd;	// Absorb the leading quote.
755 					while ( *stepEnd != 0 ) {
756 						if ( *stepEnd == quote ) {
757 							if ( *(stepEnd+1) != quote ) break;
758 							++stepEnd;
759 						}
760 						++stepEnd;
761 					}
762 					if ( *stepEnd == 0 ) {
763 						XMP_Throw ( "No terminating quote for array selector", kXMPErr_BadXPath );
764 					}
765 					++stepEnd;	// Absorb the trailing quote.
766 
767 					stepFlags = kXMP_FieldSelectorStep;	// ! Touch up later, also changing '@' to '?'.
768 
769 				}
770 
771 			}
772 
773 			if ( *stepEnd != ']' ) XMP_Throw ( "Missing ']' for array index", kXMPErr_BadXPath );
774 			++stepEnd;
775 
776 		}
777 
778 		if ( stepEnd == stepBegin ) XMP_Throw ( "Empty XPath step", kXMPErr_BadXPath );
779 		currStep.assign ( stepBegin, (stepEnd - stepBegin) );
780 
781 		if ( GetStepKind ( stepFlags ) == kXMP_StructFieldStep ) {
782 
783 			if ( currStep[0] == '@' ) {
784 				currStep[0] = '?';
785 				if ( currStep != "?xml:lang" ) XMP_Throw ( "Only xml:lang allowed with '@'", kXMPErr_BadXPath );
786 			}
787 			if ( currStep[0] == '?' ) {
788 				++qualName;
789 				stepFlags = kXMP_QualifierStep;
790 			}
791 			VerifyQualName ( qualName, nameEnd );
792 
793 		} else if ( GetStepKind ( stepFlags ) == kXMP_FieldSelectorStep ) {
794 
795 			if ( currStep[1] == '@' ) {
796 				currStep[1] = '?';
797 				if ( strncmp ( currStep.c_str(), "[?xml:lang=", 11 ) != 0 ) {
798 					XMP_Throw ( "Only xml:lang allowed with '@'", kXMPErr_BadXPath );
799 				}
800 			}
801 			if ( currStep[1] == '?' ) {
802 				++qualName;
803 				stepFlags = kXMP_QualSelectorStep;
804 			}
805 			VerifyQualName ( qualName, nameEnd );
806 
807 		}
808 
809 		expandedXPath->push_back ( XPathStepInfo ( currStep, stepFlags ) );
810 
811 	}
812 
813 }	// ExpandXPath
814 
815 // =================================================================================================
816 // FindSchemaNode
817 // ==============
818 //
819 // Find or create a schema node. Returns a pointer to the node, and optionally an iterator for the
820 // node's position in the top level vector of schema nodes. The iterator is unchanged if no schema
821 // node (null) is returned.
822 
823 XMP_Node *
FindSchemaNode(XMP_Node * xmpTree,XMP_StringPtr nsURI,bool createNodes,XMP_NodePtrPos * ptrPos)824 FindSchemaNode	( XMP_Node *		xmpTree,
825 				  XMP_StringPtr		nsURI,
826 				  bool				createNodes,
827 				  XMP_NodePtrPos *	ptrPos /* = 0 */ )
828 {
829 	XMP_Node * schemaNode = 0;
830 
831 	XMP_Assert ( xmpTree->parent == 0 );
832 
833 	for ( size_t schemaNum = 0, schemaLim = xmpTree->children.size(); schemaNum != schemaLim; ++schemaNum ) {
834 		XMP_Node * currSchema = xmpTree->children[schemaNum];
835 		XMP_Assert ( currSchema->parent == xmpTree );
836 		if ( currSchema->name == nsURI ) {
837 			schemaNode = currSchema;
838 			if ( ptrPos != 0 ) *ptrPos = xmpTree->children.begin() + schemaNum;
839 			break;
840 		}
841 	}
842 
843 	if ( (schemaNode == 0) && createNodes ) {
844 
845 		schemaNode = new XMP_Node ( xmpTree, nsURI, (kXMP_SchemaNode | kXMP_NewImplicitNode) );
846 		XMP_StringPtr prefixPtr;
847 		XMP_StringLen prefixLen;
848         bool found = XMPMeta::GetNamespacePrefix ( nsURI, &prefixPtr, &prefixLen );	// *** Use map directly?
849 		XMP_Assert ( found );
850 		UNUSED(found);
851 
852 		schemaNode->value.assign ( prefixPtr, prefixLen );
853 		xmpTree->children.push_back ( schemaNode );
854 		if ( ptrPos != 0 ) *ptrPos = xmpTree->children.end() - 1;
855 
856 		#if 0	// *** XMP_DebugBuild
857 			schemaNode->_valuePtr = schemaNode->value.c_str();
858 		#endif
859 
860 	}
861 
862 	XMP_Assert ( (ptrPos == 0) || (schemaNode == 0) || (schemaNode == **ptrPos) );
863 	XMP_Assert ( (schemaNode != 0) || (! createNodes) );
864 	return schemaNode;
865 
866 }	// FindSchemaNode
867 
868 // =================================================================================================
869 // FindChildNode
870 // =============
871 //
872 // Find or create a child node under a given parent node. Returns a pointer to the child node, and
873 // optionally an iterator for the node's position in the parent's vector of children. The iterator
874 // is unchanged if no child node (null) is returned.
875 
876 XMP_Node *
FindChildNode(XMP_Node * parent,XMP_StringPtr childName,bool createNodes,XMP_NodePtrPos * ptrPos)877 FindChildNode	( XMP_Node *		parent,
878 				  XMP_StringPtr		childName,
879 				  bool				createNodes,
880 				  XMP_NodePtrPos *	ptrPos /* = 0 */ )
881 {
882 	XMP_Node * childNode = 0;
883 
884 	if ( ! (parent->options & (kXMP_SchemaNode | kXMP_PropValueIsStruct)) ) {
885 		if ( ! (parent->options & kXMP_NewImplicitNode) ) {
886 			XMP_Throw ( "Named children only allowed for schemas and structs", kXMPErr_BadXPath );
887 		}
888 		if ( parent->options & kXMP_PropValueIsArray ) {
889 			XMP_Throw ( "Named children not allowed for arrays", kXMPErr_BadXPath );
890 		}
891 		if ( ! createNodes ) {	// *** Should be assert? If !createNodes, why is the parent a new implicit node?
892 			XMP_Throw ( "Parent is new implicit node, but createNodes is false", kXMPErr_InternalFailure );
893 		}
894 		parent->options |= kXMP_PropValueIsStruct;
895 	}
896 
897 	for ( size_t childNum = 0, childLim = parent->children.size(); childNum != childLim; ++childNum ) {
898 		XMP_Node * currChild = parent->children[childNum];
899 		XMP_Assert ( currChild->parent == parent );
900 		if ( currChild->name == childName ) {
901 			childNode = currChild;
902 			if ( ptrPos != 0 ) *ptrPos = parent->children.begin() + childNum;
903 			break;
904 		}
905 	}
906 
907 	if ( (childNode == 0) && createNodes ) {
908 		childNode = new XMP_Node ( parent, childName, kXMP_NewImplicitNode );
909 		parent->children.push_back ( childNode );
910 		if ( ptrPos != 0 ) *ptrPos = parent->children.end() - 1;
911 	}
912 
913 	XMP_Assert ( (ptrPos == 0) || (childNode == 0) || (childNode == **ptrPos) );
914 	XMP_Assert ( (childNode != 0) || (! createNodes) );
915 	return childNode;
916 
917 }	// FindChildNode
918 
919 // =================================================================================================
920 // FindQualifierNode
921 // =================
922 //
923 // Find or create a qualifier node under a given parent node. Returns a pointer to the qualifier node,
924 // and optionally an iterator for the node's position in the parent's vector of qualifiers. The iterator
925 // is unchanged if no qualifier node (null) is returned.
926 //
927 // ! On entry, the qualName parameter must not have the leading '?' from the XPath step.
928 
929 XMP_Node *
FindQualifierNode(XMP_Node * parent,XMP_StringPtr qualName,bool createNodes,XMP_NodePtrPos * ptrPos)930 FindQualifierNode	( XMP_Node *		parent,
931 					  XMP_StringPtr		qualName,
932 					  bool				createNodes,
933 					  XMP_NodePtrPos *	ptrPos /* = 0 */ )	// *** Require ptrPos internally & remove checks?
934 {
935 	XMP_Node * qualNode = 0;
936 
937 	XMP_Assert ( *qualName != '?' );
938 
939 	for ( size_t qualNum = 0, qualLim = parent->qualifiers.size(); qualNum != qualLim; ++qualNum ) {
940 		XMP_Node * currQual = parent->qualifiers[qualNum];
941 		XMP_Assert ( currQual->parent == parent );
942 		if ( currQual->name == qualName ) {
943 			qualNode = currQual;
944 			if ( ptrPos != 0 ) *ptrPos = parent->qualifiers.begin() + qualNum;
945 			break;
946 		}
947 	}
948 
949 	if ( (qualNode == 0) && createNodes ) {
950 
951 		qualNode = new XMP_Node ( parent, qualName, (kXMP_PropIsQualifier | kXMP_NewImplicitNode) );
952 		parent->options |= kXMP_PropHasQualifiers;
953 
954 		const bool isLang 	 = XMP_LitMatch ( qualName, "xml:lang" );
955 		const bool isType 	 = XMP_LitMatch ( qualName, "rdf:type" );
956 		const bool isSpecial = isLang | isType;
957 
958 		if ( isLang ) {
959 			parent->options |= kXMP_PropHasLang;
960 		} else if ( isType ) {
961 			parent->options |= kXMP_PropHasType;
962 		}
963 
964 		if ( parent->qualifiers.empty() || (! isSpecial) ) {
965 			parent->qualifiers.push_back ( qualNode );
966 			if ( ptrPos != 0 ) *ptrPos = parent->qualifiers.end() - 1;
967 		} else {
968 			XMP_NodePtrPos insertPos = parent->qualifiers.begin();	// ! Lang goes first, type after.
969 			if ( isType && (parent->options & kXMP_PropHasLang) ) ++insertPos;	// *** Does insert at end() work?
970 			insertPos = parent->qualifiers.insert ( insertPos, qualNode );
971 			if ( ptrPos != 0 ) *ptrPos = insertPos;
972 		}
973 
974 	}
975 
976 	XMP_Assert ( (ptrPos == 0) || (qualNode == 0) || (qualNode == **ptrPos) );
977 	XMP_Assert ( (qualNode != 0) || (! createNodes) );
978 	return qualNode;
979 
980 }	// FindQualifierNode
981 
982 // =================================================================================================
983 // LookupFieldSelector
984 // ===================
985 //
986 //	[fieldName="value"]	An element in an array of structs, chosen by a field value.
987 //
988 // Note that we don't create implicit nodes for field selectors, so no CreateNodes parameter.
989 
990 XMP_Index
LookupFieldSelector(const XMP_Node * arrayNode,XMP_StringPtr fieldName,XMP_StringPtr fieldValue)991 LookupFieldSelector ( const XMP_Node * arrayNode, XMP_StringPtr fieldName, XMP_StringPtr fieldValue )
992 {
993 	XMP_Index index, itemLim;
994 
995 	for ( index = 0, itemLim = arrayNode->children.size(); index != itemLim; ++index ) {
996 
997 		const XMP_Node * currItem = arrayNode->children[index];
998 		XMP_Assert ( currItem->parent == arrayNode );
999 
1000 		if ( ! (currItem->options & kXMP_PropValueIsStruct) ) {
1001 			XMP_Throw ( "Field selector must be used on array of struct", kXMPErr_BadXPath );
1002 		}
1003 
1004 		size_t f, fieldLim;
1005 		for ( f = 0, fieldLim = currItem->children.size(); f != fieldLim; ++f ) {
1006 			const XMP_Node * currField = currItem->children[f];
1007 			XMP_Assert ( currField->parent == currItem );
1008 			if ( currField->name != fieldName ) continue;
1009 			if ( currField->value == fieldValue ) break;	// Exit qual loop.
1010 		}
1011 		if ( f != fieldLim ) break;	// Exit child loop, found an item with a matching qualifier.
1012 
1013 	}
1014 
1015 	if ( index == itemLim ) index = -1;
1016 	return index;
1017 
1018 }	// LookupFieldSelector
1019 
1020 // =================================================================================================
1021 // LookupLangItem
1022 // ==============
1023 //
1024 // ! Assumes that the language value is already normalized.
1025 
1026 XMP_Index
LookupLangItem(const XMP_Node * arrayNode,XMP_VarString & lang)1027 LookupLangItem ( const XMP_Node * arrayNode, XMP_VarString & lang )
1028 {
1029 	if ( ! (arrayNode->options & kXMP_PropValueIsArray) ) {	// *** Check for alt-text?
1030 		XMP_Throw ( "Language item must be used on array", kXMPErr_BadXPath );
1031 	}
1032 
1033 	XMP_Index index   = 0;
1034 	XMP_Index itemLim = arrayNode->children.size();
1035 
1036 	for ( ; index != itemLim; ++index ) {
1037 		const XMP_Node * currItem = arrayNode->children[index];
1038 		XMP_Assert ( currItem->parent == arrayNode );
1039 		if ( currItem->qualifiers.empty() || (currItem->qualifiers[0]->name != "xml:lang") ) continue;
1040 		if ( currItem->qualifiers[0]->value == lang ) break;
1041 	}
1042 
1043 	if ( index == itemLim ) index = -1;
1044 	return index;
1045 
1046 }	// LookupLangItem
1047 
1048 // =================================================================================================
1049 // FindNode
1050 // ========
1051 //
1052 // Follow an expanded path expression to find or create a node. Returns a pointer to the node, and
1053 // optionally an iterator for the node's position in the parent's vector of children or qualifiers.
1054 // The iterator is unchanged if no child node (null) is returned.
1055 
1056 XMP_Node *
FindNode(XMP_Node * xmpTree,const XMP_ExpandedXPath & expandedXPath,bool createNodes,XMP_OptionBits leafOptions,XMP_NodePtrPos * ptrPos)1057 FindNode ( XMP_Node *		xmpTree,
1058 		   const XMP_ExpandedXPath & expandedXPath,
1059 		   bool				createNodes,
1060 		   XMP_OptionBits	leafOptions /* = 0 */,
1061 	 	   XMP_NodePtrPos * ptrPos /* = 0 */ )
1062 {
1063 	XMP_Node *     currNode = 0;
1064 	XMP_NodePtrPos currPos;
1065 	XMP_NodePtrPos newSubPos;	// Root of implicitly created subtree. Valid only if leaf is new.
1066 	bool           leafIsNew = false;
1067 
1068 	XMP_Assert ( (leafOptions == 0) || createNodes );
1069 
1070 	if ( expandedXPath.empty() ) XMP_Throw ( "Empty XPath", kXMPErr_BadXPath );
1071 
1072 	size_t stepNum = 1;	// By default start calling FollowXPathStep for the top level property step.
1073 	size_t stepLim = expandedXPath.size();
1074 
1075 	// The start of processing deals with the schema node and top level alias. If the top level step
1076 	// is not an alias, lookup the expanded path's schema URI. Otherwise, lookup the expanded path
1077 	// for the actual. While tempting, don't substitute the actual's path into the local one, don't
1078 	// risk messing with the caller's use of that. Also don't call FindNode recursively, we need to
1079 	// keep track of the root of the implicitly created subtree as we move down the path.
1080 
1081 	if ( ! (expandedXPath[kRootPropStep].options & kXMP_StepIsAlias) ) {
1082 
1083 		currNode = FindSchemaNode ( xmpTree, expandedXPath[kSchemaStep].step.c_str(), createNodes, &currPos );
1084 		if ( currNode == 0 ) return 0;
1085 
1086 		if ( currNode->options & kXMP_NewImplicitNode ) {
1087 			currNode->options ^= kXMP_NewImplicitNode;	// Clear the implicit node bit.
1088 			if ( ! leafIsNew ) newSubPos = currPos;	// Save the top most implicit node.
1089 			leafIsNew = true;	// If any parent is new, the leaf will be new also.
1090 		}
1091 
1092 	} else {
1093 
1094 		stepNum = 2;	// ! Continue processing the original path at the second level step.
1095 
1096 		XMP_AliasMapPos aliasPos = sRegisteredAliasMap->find ( expandedXPath[kRootPropStep].step );
1097 		XMP_Assert ( aliasPos != sRegisteredAliasMap->end() );
1098 
1099 		currNode = FindSchemaNode ( xmpTree, aliasPos->second[kSchemaStep].step.c_str(), createNodes, &currPos );
1100 		if ( currNode == 0 ) goto EXIT;
1101 		if ( currNode->options & kXMP_NewImplicitNode ) {
1102 			currNode->options ^= kXMP_NewImplicitNode;	// Clear the implicit node bit.
1103 			if ( ! leafIsNew ) newSubPos = currPos;	// Save the top most implicit node.
1104 			leafIsNew = true;	// If any parent is new, the leaf will be new also.
1105 		}
1106 
1107 		currNode = FollowXPathStep ( currNode, aliasPos->second, 1, createNodes, &currPos );
1108 		if ( currNode == 0 ) goto EXIT;
1109 		if ( currNode->options & kXMP_NewImplicitNode ) {
1110 			currNode->options ^= kXMP_NewImplicitNode;	// Clear the implicit node bit.
1111 			CheckImplicitStruct ( currNode, expandedXPath, 2, stepLim );
1112 			if ( ! leafIsNew ) newSubPos = currPos;	// Save the top most implicit node.
1113 			leafIsNew = true;	// If any parent is new, the leaf will be new also.
1114 		}
1115 
1116 		XMP_OptionBits arrayForm = aliasPos->second[kRootPropStep].options & kXMP_PropArrayFormMask;
1117 		XMP_Assert ( (arrayForm == 0) || (arrayForm & kXMP_PropValueIsArray) );
1118 		XMP_Assert ( (arrayForm == 0) ? (aliasPos->second.size() == 2) : (aliasPos->second.size() == 3) );
1119 
1120 		if ( arrayForm != 0 ) {
1121 			currNode = FollowXPathStep ( currNode, aliasPos->second, 2, createNodes, &currPos, true );
1122 			if ( currNode == 0 ) goto EXIT;
1123 			if ( currNode->options & kXMP_NewImplicitNode ) {
1124 				currNode->options ^= kXMP_NewImplicitNode;	// Clear the implicit node bit.
1125 				CheckImplicitStruct ( currNode, expandedXPath, 2, stepLim );
1126 				if ( ! leafIsNew ) newSubPos = currPos;	// Save the top most implicit node.
1127 				leafIsNew = true;	// If any parent is new, the leaf will be new also.
1128 			}
1129 		}
1130 
1131 	}
1132 
1133 	// Now follow the remaining steps of the original XPath.
1134 
1135 	// *** ??? Change all the num/lim loops back to num<lim? Probably safer.
1136 
1137 	try {
1138 		for ( ; stepNum < stepLim; ++stepNum ) {
1139 			currNode = FollowXPathStep ( currNode, expandedXPath, stepNum, createNodes, &currPos );
1140 			if ( currNode == 0 ) goto EXIT;
1141 			if ( currNode->options & kXMP_NewImplicitNode ) {
1142 				currNode->options ^= kXMP_NewImplicitNode;	// Clear the implicit node bit.
1143 				CheckImplicitStruct ( currNode, expandedXPath, stepNum+1, stepLim );
1144 				if ( ! leafIsNew ) newSubPos = currPos;	// Save the top most implicit node.
1145 				leafIsNew = true;	// If any parent is new, the leaf will be new also.
1146 			}
1147 		}
1148 	} catch ( ... ) {
1149 		if ( leafIsNew ) DeleteSubtree ( newSubPos );
1150 		throw;
1151 	}
1152 
1153 	// Done. Delete the implicitly created subtree if the eventual node was not found.
1154 
1155 EXIT:
1156 
1157 	XMP_Assert ( (currNode == 0) || (currNode == *currPos) );
1158 	XMP_Assert ( (currNode != 0) || (! createNodes) );
1159 
1160 	if ( leafIsNew ) {
1161 		if ( currNode != 0 ) {
1162 			currNode->options |= leafOptions;
1163 		} else {
1164 			DeleteSubtree ( newSubPos );
1165 		}
1166 	}
1167 
1168 	if ( (currNode != 0) && (ptrPos != 0) ) *ptrPos = currPos;
1169 	return currNode;
1170 
1171 }	// FindNode
1172 
1173 // =================================================================================================
1174 // CloneOffspring
1175 // ==============
1176 
1177 void
CloneOffspring(const XMP_Node * origParent,XMP_Node * cloneParent)1178 CloneOffspring ( const XMP_Node * origParent, XMP_Node * cloneParent )
1179 {
1180 	size_t qualCount  = origParent->qualifiers.size();
1181 	size_t childCount = origParent->children.size();
1182 
1183 	if ( qualCount > 0 ) {
1184 
1185 		cloneParent->qualifiers.reserve ( qualCount );
1186 
1187 		for ( size_t qualNum = 0, qualLim = qualCount; qualNum != qualLim; ++qualNum ) {
1188 			const XMP_Node * origQual  = origParent->qualifiers[qualNum];
1189 			XMP_Node *       cloneQual = new XMP_Node ( cloneParent, origQual->name, origQual->value, origQual->options );
1190 			CloneOffspring ( origQual, cloneQual );
1191 			cloneParent->qualifiers.push_back ( cloneQual );
1192 		}
1193 
1194 	}
1195 
1196 	if ( childCount > 0 ) {
1197 
1198 		cloneParent->children.reserve ( childCount );
1199 
1200 		for ( size_t childNum = 0, childLim = childCount; childNum != childLim; ++childNum ) {
1201 			const XMP_Node * origChild  = origParent->children[childNum];
1202 			XMP_Node *       cloneChild = new XMP_Node ( cloneParent, origChild->name, origChild->value, origChild->options );
1203 			CloneOffspring ( origChild, cloneChild );
1204 			cloneParent->children.push_back ( cloneChild );
1205 		}
1206 
1207 	}
1208 
1209 }	// CloneOffspring
1210 
1211 // =================================================================================================
1212 // CloneSubtree
1213 // ============
1214 
1215 XMP_Node *
CloneSubtree(const XMP_Node * origRoot,XMP_Node * cloneParent)1216 CloneSubtree ( const XMP_Node * origRoot, XMP_Node * cloneParent )
1217 {
1218 	#if XMP_DebugBuild
1219 		if ( cloneParent->parent == 0 ) {
1220 			XMP_Assert ( origRoot->options & kXMP_SchemaNode );
1221 			XMP_Assert ( FindConstSchema ( cloneParent, origRoot->name.c_str() ) == 0 );
1222 		} else {
1223 			XMP_Assert ( ! (origRoot->options & kXMP_SchemaNode) );
1224 			if ( cloneParent->options & kXMP_PropValueIsStruct ) {	// Might be an array.
1225 				XMP_Assert ( FindConstChild ( cloneParent, origRoot->name.c_str() ) == 0 );
1226 			}
1227 		}
1228 	#endif
1229 
1230 	XMP_Node * cloneRoot = new XMP_Node ( cloneParent, origRoot->name, origRoot->value, origRoot->options );
1231 	CloneOffspring ( origRoot, cloneRoot ) ;
1232 	cloneParent->children.push_back ( cloneRoot );
1233 
1234 	return cloneRoot;
1235 
1236 }	// CloneSubtree
1237 
1238 // =================================================================================================
1239 // CompareSubtrees
1240 // ===============
1241 //
1242 // Compare 2 subtrees for semantic equality. The comparison includes value, qualifiers, and form.
1243 // Schemas, top level properties, struct fields, and qualifiers are allowed to have differing order,
1244 // the appropriate right node is found from the left node's name. Alt-text arrays are allowed to be
1245 // in differing language order, other arrays are compared in order.
1246 
1247 // *** Might someday consider sorting unordered arrays.
1248 // *** Should expose this through XMPUtils.
1249 
1250 bool
CompareSubtrees(const XMP_Node & leftNode,const XMP_Node & rightNode)1251 CompareSubtrees ( const XMP_Node & leftNode, const XMP_Node & rightNode )
1252 {
1253 	// Don't compare the names here, we want to allow the outermost roots to have different names.
1254 	if ( (leftNode.value != rightNode.value) ||
1255 	     (leftNode.options != rightNode.options) ||
1256 	     (leftNode.children.size() != rightNode.children.size()) ||
1257 	     (leftNode.qualifiers.size() != rightNode.qualifiers.size()) ) return false;
1258 
1259 	// Compare the qualifiers, allowing them to be out of order.
1260 	for ( size_t qualNum = 0, qualLim = leftNode.qualifiers.size(); qualNum != qualLim; ++qualNum ) {
1261 		const XMP_Node * leftQual  = leftNode.qualifiers[qualNum];
1262 		const XMP_Node * rightQual = FindConstQualifier ( &rightNode, leftQual->name.c_str() );
1263 		if ( (rightQual == 0) || (! CompareSubtrees ( *leftQual, *rightQual )) ) return false;
1264 	}
1265 
1266 	if ( (leftNode.parent == 0) || (leftNode.options & (kXMP_SchemaNode | kXMP_PropValueIsStruct)) ) {
1267 
1268 		// The parent node is a tree root, a schema, or a struct.
1269 		for ( size_t childNum = 0, childLim = leftNode.children.size(); childNum != childLim; ++childNum ) {
1270 			const XMP_Node * leftChild  = leftNode.children[childNum];
1271 			const XMP_Node * rightChild = FindConstChild ( &rightNode, leftChild->name.c_str() );
1272 			if ( (rightChild == 0) || (! CompareSubtrees ( *leftChild, *rightChild )) ) return false;
1273 		}
1274 
1275 	} else if ( leftNode.options & kXMP_PropArrayIsAltText ) {
1276 
1277 		// The parent node is an alt-text array.
1278 		for ( size_t childNum = 0, childLim = leftNode.children.size(); childNum != childLim; ++childNum ) {
1279 			const XMP_Node * leftChild = leftNode.children[childNum];
1280 			XMP_Assert ( (! leftChild->qualifiers.empty()) && (leftChild->qualifiers[0]->name == "xml:lang") );
1281 			XMP_Index rightIndex = LookupLangItem ( &rightNode, leftChild->qualifiers[0]->value );
1282 			if ( rightIndex == -1 ) return false;
1283 			const XMP_Node * rightChild = rightNode.children[rightIndex];
1284 			if ( ! CompareSubtrees ( *leftChild, *rightChild ) ) return false;
1285 		}
1286 
1287 	} else {
1288 
1289 		// The parent must be simple or some other (not alt-text) kind of array.
1290 		XMP_Assert ( (! (leftNode.options & kXMP_PropCompositeMask)) || (leftNode.options & kXMP_PropValueIsArray) );
1291 		for ( size_t childNum = 0, childLim = leftNode.children.size(); childNum != childLim; ++childNum ) {
1292 			const XMP_Node * leftChild  = leftNode.children[childNum];
1293 			const XMP_Node * rightChild = rightNode.children[childNum];
1294 			if ( ! CompareSubtrees ( *leftChild, *rightChild ) ) return false;
1295 		}
1296 
1297 	}
1298 
1299 	return true;
1300 
1301 }	// CompareSubtrees
1302 
1303 // =================================================================================================
1304 // DeleteEmptySchema
1305 // =================
1306 
1307 void
DeleteEmptySchema(XMP_Node * schemaNode)1308 DeleteEmptySchema ( XMP_Node * schemaNode )
1309 {
1310 
1311 	if ( XMP_NodeIsSchema ( schemaNode->options ) && schemaNode->children.empty() ) {
1312 
1313 		XMP_Node * xmpTree = schemaNode->parent;
1314 
1315 		size_t schemaNum = 0;
1316 		size_t schemaLim = xmpTree->children.size();
1317 		while ( (schemaNum < schemaLim) && (xmpTree->children[schemaNum] != schemaNode) ) ++schemaNum;
1318 		XMP_Assert ( schemaNum < schemaLim );
1319 
1320 		XMP_NodePtrPos schemaPos = xmpTree->children.begin() + schemaNum;
1321 		XMP_Assert ( *schemaPos == schemaNode );
1322 
1323 		xmpTree->children.erase ( schemaPos );
1324 		delete schemaNode;
1325 
1326 	}
1327 
1328 }	// DeleteEmptySchema
1329 
1330 // =================================================================================================
1331 // NormalizeLangValue
1332 // ==================
1333 //
1334 // Normalize an xml:lang value so that comparisons are effectively case insensitive as required by
1335 // RFC 3066 (which superceeds RFC 1766). The normalization rules:
1336 //
1337 //	- The primary subtag is lower case, the suggested practice of ISO 639.
1338 //	- All 2 letter secondary subtags are upper case, the suggested practice of ISO 3166.
1339 //	- All other subtags are lower case.
1340 
1341 void
NormalizeLangValue(XMP_VarString * value)1342 NormalizeLangValue ( XMP_VarString * value )
1343 {
1344 	char * tagStart;
1345 	char * tagEnd;
1346 
1347 	// Find and process the primary subtag.
1348 
1349 	tagStart = (char*) value->c_str();
1350 	for ( tagEnd = tagStart; (*tagEnd != 0) && (*tagEnd != '-'); ++tagEnd ) {
1351 		if ( ('A' <= *tagEnd) && (*tagEnd <= 'Z') ) *tagEnd += 0x20;
1352 	}
1353 
1354 	// Find and process the secondary subtag.
1355 
1356 	tagStart = tagEnd;
1357 	if ( *tagStart == '-' ) ++tagStart;
1358 	for ( tagEnd = tagStart; (*tagEnd != 0) && (*tagEnd != '-'); ++tagEnd ) {
1359 		if ( ('A' <= *tagEnd) && (*tagEnd <= 'Z') ) *tagEnd += 0x20;
1360 	}
1361 	if ( tagEnd == tagStart+2 ) {
1362 		if ( ('a' <= *tagStart) && (*tagStart <= 'z') ) *tagStart -= 0x20;
1363 		++tagStart;
1364 		if ( ('a' <= *tagStart) && (*tagStart <= 'z') ) *tagStart -= 0x20;
1365 	}
1366 
1367 	// Find and process the remaining subtags.
1368 
1369 	while ( true ) {
1370 		tagStart = tagEnd;
1371 		if ( *tagStart == '-' ) ++tagStart;
1372 		if ( *tagStart == 0 ) break;
1373 		for ( tagEnd = tagStart; (*tagEnd != 0) && (*tagEnd != '-'); ++tagEnd ) {
1374 			if ( ('A' <= *tagEnd) && (*tagEnd <= 'Z') ) *tagEnd += 0x20;
1375 		}
1376 	}
1377 
1378 }	// NormalizeLangValue
1379 
1380 // =================================================================================================
1381 // NormalizeLangArray
1382 // ==================
1383 //
1384 // Make sure the x-default item is first. Touch up "single value" arrays that have a default plus
1385 // one real language. This case should have the same value for both items. Older Adobe apps were
1386 // hardwired to only use the 'x-default' item, so we copy that value to the other item.
1387 
1388 void
NormalizeLangArray(XMP_Node * array)1389 NormalizeLangArray ( XMP_Node * array )
1390 {
1391 	XMP_Assert ( XMP_ArrayIsAltText(array->options) );
1392 
1393 	size_t itemNum;
1394 	size_t itemLim = array->children.size();
1395 	bool   hasDefault = false;
1396 
1397 	for ( itemNum = 0; itemNum < itemLim; ++itemNum ) {
1398 
1399 		if ( array->children[itemNum]->qualifiers.empty() ||
1400 			 (array->children[itemNum]->qualifiers[0]->name != "xml:lang") ) {
1401 			XMP_Throw ( "AltText array items must have an xml:lang qualifier", kXMPErr_BadXMP );
1402 		}
1403 
1404 		if ( array->children[itemNum]->qualifiers[0]->value == "x-default" ) {
1405 			hasDefault = true;
1406 			break;
1407 		}
1408 
1409 	}
1410 
1411 	if ( hasDefault ) {
1412 
1413 		if ( itemNum != 0 ) {
1414 			XMP_Node * temp = array->children[0];
1415 			array->children[0] = array->children[itemNum];
1416 			array->children[itemNum] = temp;
1417 		}
1418 
1419 // 09-Oct-07, ahu: disabled to avoid unexpected behaviour
1420 //		if ( itemLim == 2 ) array->children[1]->value = array->children[0]->value;
1421 
1422 	}
1423 
1424 }	// NormalizeLangArray
1425 
1426 // =================================================================================================
1427 // DetectAltText
1428 // =============
1429 //
1430 // See if an array is an alt-text array. If so, make sure the x-default item is first.
1431 
1432 void
DetectAltText(XMP_Node * xmpParent)1433 DetectAltText ( XMP_Node * xmpParent )
1434 {
1435 	XMP_Assert ( XMP_ArrayIsAlternate(xmpParent->options) );
1436 
1437 	size_t itemNum, itemLim;
1438 
1439 	for ( itemNum = 0, itemLim = xmpParent->children.size(); itemNum < itemLim; ++itemNum ) {
1440 		XMP_OptionBits currOptions = xmpParent->children[itemNum]->options;
1441 		if ( (currOptions & kXMP_PropCompositeMask) || (! (currOptions & kXMP_PropHasLang)) ) break;
1442 	}
1443 
1444 	if ( (itemLim != 0) && (itemNum == itemLim) ) {
1445 		xmpParent->options |= kXMP_PropArrayIsAltText;
1446 		NormalizeLangArray ( xmpParent );
1447 	}
1448 
1449 }	// DetectAltText
1450 
1451 // =================================================================================================
1452 // SortNamedNodes
1453 // ==============
1454 //
1455 // Sort the pointers in an XMP_NodeOffspring vector by name.
1456 
Compare(const XMP_Node * left,const XMP_Node * right)1457 static inline bool Compare ( const XMP_Node * left, const XMP_Node * right )
1458 {
1459 	return (left->name < right->name);
1460 }
1461 
1462 void
SortNamedNodes(XMP_NodeOffspring & nodeVector)1463 SortNamedNodes ( XMP_NodeOffspring & nodeVector )
1464 {
1465 	sort ( nodeVector.begin(), nodeVector.end(), Compare );
1466 }	// SortNamedNodes
1467 
1468 // =================================================================================================
1469