1 // =================================================================================================
2 // Copyright 2002-2008 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 // Adobe patent application tracking #P435, entitled 'Unique markers to simplify embedding data of
9 // one format in a file with a different format', inventors: Sean Parent, Greg Gilley.
10 // =================================================================================================
11 
12 #include "XMP_Environment.h"	// ! This must be the first include!
13 #include "XMPCore_Impl.hpp"
14 
15 #include "XMPMeta.hpp"
16 #include "XMPIterator.hpp"
17 #include "XMPUtils.hpp"
18 
19 #include "XMP_Version.h"
20 #include "UnicodeInlines.incl_cpp"
21 #include "UnicodeConversions.hpp"
22 #include "ExpatAdapter.hpp"
23 
24 #if XMP_DebugBuild
25 	#include <iostream>
26 #endif
27 
28 using namespace std;
29 
30 #if XMP_WinBuild
31 	#pragma warning ( disable : 4533 )	// initialization of '...' is skipped by 'goto ...'
32 	#pragma warning ( disable : 4702 )	// unreachable code
33 	#pragma warning ( disable : 4800 )	// forcing value to bool 'true' or 'false' (performance warning)
34 #endif
35 
36 
37 // *** Use the XMP_PropIsXyz (Schema, Simple, Struct, Array, ...) macros
38 // *** Add debug codegen checks, e.g. that typical masking operations really work
39 // *** Change all uses of strcmp and strncmp to XMP_LitMatch and XMP_LitNMatch
40 
41 
42 // =================================================================================================
43 // Local Types and Constants
44 // =========================
45 
46 typedef unsigned char XMP_CLTMatch;
47 
48 enum {	// Values for XMP_CLTMatch.
49 	kXMP_CLT_NoValues,
50 	kXMP_CLT_SpecificMatch,
51 	kXMP_CLT_SingleGeneric,
52 	kXMP_CLT_MultipleGeneric,
53 	kXMP_CLT_XDefault,
54 	kXMP_CLT_FirstItem
55 };
56 
57 
58 // =================================================================================================
59 // Static Variables
60 // ================
61 
62 
63 // =================================================================================================
64 // Local Utilities
65 // ===============
66 
67 
68 // -------------------------------------------------------------------------------------------------
69 // SetNodeValue
70 // ------------
71 
72 static inline void
SetNodeValue(XMP_Node * node,XMP_StringPtr value)73 SetNodeValue ( XMP_Node * node, XMP_StringPtr value )
74 {
75 
76 	#if XMP_DebugBuild	// ! Hack to force an assert.
77 		if ( (node->name == "xmp:TestAssertNotify") && XMP_LitMatch ( value, "DoIt!" ) ) {
78 			XMP_Assert ( node->name != "xmp:TestAssertNotify" );
79 		}
80 	#endif
81 
82 	node->value = value;
83 
84 	XMP_Uns8* chPtr = (XMP_Uns8*) node->value.c_str();	// Check for valid UTF-8, replace ASCII controls with a space.
85 	while ( *chPtr != 0 ) {
86 		while ( (*chPtr != 0) && (*chPtr < 0x80) ) {
87 			if ( *chPtr < 0x20 ) {
88 				if ( (*chPtr != kTab) && (*chPtr != kLF) && (*chPtr != kCR) ) *chPtr = 0x20;
89 			} else if (*chPtr == 0x7F ) {
90 				*chPtr = 0x20;
91 			}
92 			++chPtr;
93 		}
94 		XMP_Assert ( (*chPtr == 0) || (*chPtr >= 0x80) );
95 		if ( *chPtr != 0 ) (void) GetCodePoint ( (const XMP_Uns8 **) &chPtr );	// Throws for bad UTF-8.
96 	}
97 
98 	if ( XMP_PropIsQualifier(node->options) && (node->name == "xml:lang") ) NormalizeLangValue ( &node->value );
99 
100 	#if 0	// *** XMP_DebugBuild
101 		node->_valuePtr = node->value.c_str();
102 	#endif
103 
104 }	// SetNodeValue
105 
106 
107 // -------------------------------------------------------------------------------------------------
108 // SetNode
109 // -------
110 //
111 // The internals for SetProperty and related calls, used after the node is found or created.
112 
113 static void
SetNode(XMP_Node * node,XMP_StringPtr value,XMP_OptionBits options)114 SetNode	( XMP_Node * node, XMP_StringPtr value, XMP_OptionBits options )
115 {
116 	if ( options & kXMP_DeleteExisting ) {
117 		XMP_ClearOption ( options, kXMP_DeleteExisting );
118 		node->options = options;
119 		node->value.erase();
120 		node->RemoveChildren();
121 		node->RemoveQualifiers();
122 	}
123 
124 	node->options |= options;	// Keep options set by FindNode when creating a new node.
125 
126 	if ( value != 0 ) {
127 
128 		// This is setting the value of a leaf node.
129 		if ( node->options & kXMP_PropCompositeMask ) XMP_Throw ( "Composite nodes can't have values", kXMPErr_BadXPath );
130 		XMP_Assert ( node->children.empty() );
131 		SetNodeValue ( node, value );
132 
133 	} else {
134 
135 		// This is setting up an array or struct.
136 		if ( ! node->value.empty() ) XMP_Throw ( "Composite nodes can't have values", kXMPErr_BadXPath );
137 		if ( node->options & kXMP_PropCompositeMask ) {	// Can't change an array to a struct, or vice versa.
138 			if ( (options & kXMP_PropCompositeMask) != (node->options & kXMP_PropCompositeMask) ) {
139 				XMP_Throw ( "Requested and existing composite form mismatch", kXMPErr_BadXPath );
140 			}
141 		}
142 		node->RemoveChildren();
143 
144 	}
145 
146 }	// SetNode
147 
148 
149 // -------------------------------------------------------------------------------------------------
150 // DoSetArrayItem
151 // --------------
152 
153 static void
DoSetArrayItem(XMP_Node * arrayNode,XMP_Index itemIndex,XMP_StringPtr itemValue,XMP_OptionBits options)154 DoSetArrayItem ( XMP_Node *		arrayNode,
155 				 XMP_Index		itemIndex,
156 				 XMP_StringPtr	itemValue,
157 				 XMP_OptionBits options )
158 {
159 	XMP_OptionBits itemLoc = options & kXMP_PropArrayLocationMask;
160 	XMP_Index      arraySize = arrayNode->children.size();
161 
162 	options &= ~kXMP_PropArrayLocationMask;
163 	options = VerifySetOptions ( options, itemValue );
164 
165 	// Now locate or create the item node and set the value. Note the index parameter is one-based!
166 	// The index can be in the range [0..size+1] or "last", normalize it and check the insert flags.
167 	// The order of the normalization checks is important. If the array is empty we end up with an
168 	// index and location to set item size+1.
169 
170 	XMP_Node * itemNode = 0;
171 
172 	if ( itemIndex == kXMP_ArrayLastItem ) itemIndex = arraySize;
173 	if ( (itemIndex == 0) && (itemLoc == kXMP_InsertAfterItem) ) {
174 		itemIndex = 1;
175 		itemLoc = kXMP_InsertBeforeItem;
176 	}
177 	if ( (itemIndex == arraySize) && (itemLoc == kXMP_InsertAfterItem) ) {
178 		itemIndex += 1;
179 		itemLoc = 0;
180 	}
181 	if ( (itemIndex == arraySize+1) && (itemLoc == kXMP_InsertBeforeItem) ) itemLoc = 0;
182 
183 	if ( itemIndex == arraySize+1 ) {
184 
185 		if ( itemLoc != 0 ) XMP_Throw ( "Can't insert before or after implicit new item", kXMPErr_BadIndex );
186 		itemNode = new XMP_Node ( arrayNode, kXMP_ArrayItemName, 0 );
187 		arrayNode->children.push_back ( itemNode );
188 
189 	} else {
190 
191 		if ( (itemIndex < 1) || (itemIndex > arraySize) ) XMP_Throw ( "Array index out of bounds", kXMPErr_BadIndex );
192 		--itemIndex;	// ! Convert the index to a C zero-based value!
193 		if ( itemLoc == 0 ) {
194 			itemNode = arrayNode->children[itemIndex];
195 		} else {
196 			XMP_NodePtrPos itemPos = arrayNode->children.begin() + itemIndex;
197 			if ( itemLoc == kXMP_InsertAfterItem ) ++itemPos;
198 			itemNode = new XMP_Node ( arrayNode, kXMP_ArrayItemName, 0 );
199 			itemPos = arrayNode->children.insert ( itemPos, itemNode );
200 		}
201 
202 	}
203 
204 	SetNode ( itemNode, itemValue, options );
205 
206 }	// DoSetArrayItem
207 
208 
209 // -------------------------------------------------------------------------------------------------
210 // ChooseLocalizedText
211 // -------------------
212 //
213 // 1. Look for an exact match with the specific language.
214 // 2. If a generic language is given, look for partial matches.
215 // 3. Look for an "x-default" item.
216 // 4. Choose the first item.
217 
218 static XMP_CLTMatch
ChooseLocalizedText(const XMP_Node * arrayNode,XMP_StringPtr genericLang,XMP_StringPtr specificLang,const XMP_Node ** itemNode)219 ChooseLocalizedText ( const XMP_Node *	 arrayNode,
220 					  XMP_StringPtr		 genericLang,
221 					  XMP_StringPtr		 specificLang,
222 					  const XMP_Node * * itemNode )
223 {
224 	const XMP_Node * currItem = 0;
225 	const size_t itemLim = arrayNode->children.size();
226 	size_t itemNum;
227 
228 	// See if the array has the right form. Allow empty alt arrays, that is what parsing returns.
229 	// *** Should check alt-text bit when that is reliably maintained.
230 
231 	if ( ! ( XMP_ArrayIsAltText(arrayNode->options) ||
232 	         (arrayNode->children.empty() && XMP_ArrayIsAlternate(arrayNode->options)) ) ) {
233 		XMP_Throw ( "Localized text array is not alt-text", kXMPErr_BadXPath );
234 	}
235 	if ( arrayNode->children.empty() ) {
236 		*itemNode = 0;
237 		return kXMP_CLT_NoValues;
238 	}
239 
240 	for ( itemNum = 0; itemNum < itemLim; ++itemNum ) {
241 		currItem = arrayNode->children[itemNum];
242 		if ( currItem->options & kXMP_PropCompositeMask ) {
243 			XMP_Throw ( "Alt-text array item is not simple", kXMPErr_BadXPath );
244 		}
245 		if ( currItem->qualifiers.empty() || (currItem->qualifiers[0]->name != "xml:lang") ) {
246 			XMP_Throw ( "Alt-text array item has no language qualifier", kXMPErr_BadXPath );
247 		}
248 	}
249 
250 	// Look for an exact match with the specific language.
251 	for ( itemNum = 0; itemNum < itemLim; ++itemNum ) {
252 		currItem = arrayNode->children[itemNum];
253 		if ( currItem->qualifiers[0]->value == specificLang ) {
254 			*itemNode = currItem;
255 			return kXMP_CLT_SpecificMatch;
256 		}
257 	}
258 
259 	if ( *genericLang != 0 ) {
260 
261 		// Look for the first partial match with the generic language.
262 		const size_t genericLen = strlen ( genericLang );
263 		for ( itemNum = 0; itemNum < itemLim; ++itemNum ) {
264 			currItem = arrayNode->children[itemNum];
265 			XMP_StringPtr currLang = currItem->qualifiers[0]->value.c_str();
266 			const size_t currLangSize = currItem->qualifiers[0]->value.size();
267 			if ( (currLangSize >= genericLen) &&
268 				 XMP_LitNMatch ( currLang, genericLang, genericLen ) &&
269 				 ((currLangSize == genericLen) || (currLang[genericLen] == '-')) ) {
270 				*itemNode = currItem;
271 				break;	// ! Don't return, need to look for other matches.
272 			}
273 		}
274 
275 		if ( itemNum < itemLim ) {
276 
277 			// Look for a second partial match with the generic language.
278 			for ( ++itemNum; itemNum < itemLim; ++itemNum ) {
279 				currItem = arrayNode->children[itemNum];
280 				XMP_StringPtr currLang = currItem->qualifiers[0]->value.c_str();
281 				const size_t currLangSize = currItem->qualifiers[0]->value.size();
282 				if ( (currLangSize >= genericLen) &&
283 					 XMP_LitNMatch ( currLang, genericLang, genericLen ) &&
284 					 ((currLangSize == genericLen) || (currLang[genericLen] == '-')) ) {
285 					return kXMP_CLT_MultipleGeneric;	// ! Leave itemNode with the first partial match.
286 				}
287 			}
288 			return kXMP_CLT_SingleGeneric;	// No second partial match was found.
289 
290 		}
291 
292 	}
293 
294 	// Look for an 'x-default' item.
295 	for ( itemNum = 0; itemNum < itemLim; ++itemNum ) {
296 		currItem = arrayNode->children[itemNum];
297 		if ( currItem->qualifiers[0]->value == "x-default" ) {
298 			*itemNode = currItem;
299 			return kXMP_CLT_XDefault;
300 		}
301 	}
302 
303 	// Everything failed, choose the first item.
304 	*itemNode = arrayNode->children[0];
305 	return kXMP_CLT_FirstItem;
306 
307 }	// ChooseLocalizedText
308 
309 
310 // -------------------------------------------------------------------------------------------------
311 // AppendLangItem
312 // --------------
313 
314 static void
AppendLangItem(XMP_Node * arrayNode,XMP_StringPtr itemLang,XMP_StringPtr itemValue)315 AppendLangItem ( XMP_Node * arrayNode, XMP_StringPtr itemLang, XMP_StringPtr itemValue )
316 {
317 	XMP_Node * newItem  = new XMP_Node ( arrayNode, kXMP_ArrayItemName, itemValue, (kXMP_PropHasQualifiers | kXMP_PropHasLang) );
318 	XMP_Node * langQual = new XMP_Node ( newItem, "xml:lang", itemLang, kXMP_PropIsQualifier );
319 	newItem->qualifiers.push_back ( langQual );
320 
321 	if ( (arrayNode->children.empty()) || (langQual->value != "x-default") ) {
322 		arrayNode->children.push_back ( newItem );
323 	} else {
324 		arrayNode->children.insert ( arrayNode->children.begin(), newItem );
325 	}
326 
327 }	// AppendLangItem
328 
329 
330 // =================================================================================================
331 // Class Methods
332 // =============
333 //
334 //
335 // =================================================================================================
336 
337 
338 // -------------------------------------------------------------------------------------------------
339 // GetProperty
340 // -----------
341 
342 bool
GetProperty(XMP_StringPtr schemaNS,XMP_StringPtr propName,XMP_StringPtr * propValue,XMP_StringLen * valueSize,XMP_OptionBits * options) const343 XMPMeta::GetProperty ( XMP_StringPtr	schemaNS,
344 					   XMP_StringPtr	propName,
345 					   XMP_StringPtr *	propValue,
346 					   XMP_StringLen *	valueSize,
347 					   XMP_OptionBits *	options ) const
348 {
349 	XMP_Assert ( (schemaNS != 0) && (propName != 0) );	// Enforced by wrapper.
350 	XMP_Assert ( (propValue != 0) && (valueSize != 0) && (options != 0) );	// Enforced by wrapper.
351 
352 	XMP_ExpandedXPath expPath;
353 	ExpandXPath ( schemaNS, propName, &expPath );
354 
355 	XMP_Node * propNode = FindConstNode ( &tree, expPath );
356 	if ( propNode == 0 ) return false;
357 
358 	*propValue = propNode->value.c_str();
359 	*valueSize = propNode->value.size();
360 	*options   = propNode->options;
361 
362 	return true;
363 
364 }	// GetProperty
365 
366 
367 // -------------------------------------------------------------------------------------------------
368 // GetArrayItem
369 // ------------
370 
371 bool
GetArrayItem(XMP_StringPtr schemaNS,XMP_StringPtr arrayName,XMP_Index itemIndex,XMP_StringPtr * itemValue,XMP_StringLen * valueSize,XMP_OptionBits * options) const372 XMPMeta::GetArrayItem ( XMP_StringPtr	 schemaNS,
373 						XMP_StringPtr	 arrayName,
374 						XMP_Index		 itemIndex,
375 						XMP_StringPtr *	 itemValue,
376 						XMP_StringLen *	 valueSize,
377 						XMP_OptionBits * options ) const
378 {
379 	XMP_Assert ( (schemaNS != 0) && (arrayName != 0) );	// Enforced by wrapper.
380 	XMP_Assert ( (itemValue != 0) && (valueSize != 0) && (options != 0) );	// Enforced by wrapper.
381 
382 	XMP_StringPtr itemPath;
383 	XMP_StringLen pathLen;
384 
385 	XMPUtils::ComposeArrayItemPath ( schemaNS, arrayName, itemIndex, &itemPath, &pathLen );
386 	return GetProperty ( schemaNS, itemPath, itemValue, valueSize, options );
387 
388 }	// GetArrayItem
389 
390 
391 // -------------------------------------------------------------------------------------------------
392 // GetStructField
393 // --------------
394 
395 bool
GetStructField(XMP_StringPtr schemaNS,XMP_StringPtr structName,XMP_StringPtr fieldNS,XMP_StringPtr fieldName,XMP_StringPtr * fieldValue,XMP_StringLen * valueSize,XMP_OptionBits * options) const396 XMPMeta::GetStructField	( XMP_StringPtr	   schemaNS,
397 						  XMP_StringPtr	   structName,
398 						  XMP_StringPtr	   fieldNS,
399 						  XMP_StringPtr	   fieldName,
400 						  XMP_StringPtr *  fieldValue,
401 						  XMP_StringLen *  valueSize,
402 						  XMP_OptionBits * options ) const
403 {
404 	XMP_Assert ( (schemaNS != 0) && (structName != 0) && (fieldNS != 0) && (fieldName != 0) );	// Enforced by wrapper.
405 	XMP_Assert ( (fieldValue != 0) && (valueSize != 0) && (options != 0) );	// Enforced by wrapper.
406 
407 	XMP_StringPtr fieldPath;
408 	XMP_StringLen pathLen;
409 
410 	XMPUtils::ComposeStructFieldPath ( schemaNS, structName, fieldNS, fieldName, &fieldPath, &pathLen );
411 	return GetProperty ( schemaNS, fieldPath, fieldValue, valueSize, options );
412 
413 }	// GetStructField
414 
415 
416 // -------------------------------------------------------------------------------------------------
417 // GetQualifier
418 // ------------
419 
420 bool
GetQualifier(XMP_StringPtr schemaNS,XMP_StringPtr propName,XMP_StringPtr qualNS,XMP_StringPtr qualName,XMP_StringPtr * qualValue,XMP_StringLen * valueSize,XMP_OptionBits * options) const421 XMPMeta::GetQualifier ( XMP_StringPtr	 schemaNS,
422 						XMP_StringPtr	 propName,
423 						XMP_StringPtr	 qualNS,
424 						XMP_StringPtr	 qualName,
425 						XMP_StringPtr *	 qualValue,
426 						XMP_StringLen *	 valueSize,
427 						XMP_OptionBits * options ) const
428 {
429 	XMP_Assert ( (schemaNS != 0) && (propName != 0) && (qualNS != 0) && (qualName != 0) );	// Enforced by wrapper.
430 	XMP_Assert ( (qualValue != 0) && (valueSize != 0) && (options != 0) );	// Enforced by wrapper.
431 
432 	XMP_StringPtr qualPath;
433 	XMP_StringLen pathLen;
434 
435 	XMPUtils::ComposeQualifierPath ( schemaNS, propName, qualNS, qualName, &qualPath, &pathLen );
436 	return GetProperty ( schemaNS, qualPath, qualValue, valueSize, options );
437 
438 }	// GetQualifier
439 
440 
441 // -------------------------------------------------------------------------------------------------
442 // SetProperty
443 // -----------
444 
445 // *** Should handle array items specially, calling SetArrayItem.
446 
447 void
SetProperty(XMP_StringPtr schemaNS,XMP_StringPtr propName,XMP_StringPtr propValue,XMP_OptionBits options)448 XMPMeta::SetProperty ( XMP_StringPtr  schemaNS,
449 					   XMP_StringPtr  propName,
450 					   XMP_StringPtr  propValue,
451 					   XMP_OptionBits options )
452 {
453 	XMP_Assert ( (schemaNS != 0) && (propName != 0) );	// Enforced by wrapper.
454 
455 	options = VerifySetOptions ( options, propValue );
456 
457 	XMP_ExpandedXPath expPath;
458 	ExpandXPath ( schemaNS, propName, &expPath );
459 
460 	XMP_Node * propNode = FindNode ( &tree, expPath, kXMP_CreateNodes, options );
461 	if ( propNode == 0 ) XMP_Throw ( "Specified property does not exist", kXMPErr_BadXPath );
462 
463 	SetNode ( propNode, propValue, options );
464 
465 }	// SetProperty
466 
467 
468 // -------------------------------------------------------------------------------------------------
469 // SetArrayItem
470 // ------------
471 
472 void
SetArrayItem(XMP_StringPtr schemaNS,XMP_StringPtr arrayName,XMP_Index itemIndex,XMP_StringPtr itemValue,XMP_OptionBits options)473 XMPMeta::SetArrayItem ( XMP_StringPtr  schemaNS,
474 						XMP_StringPtr  arrayName,
475 						XMP_Index	   itemIndex,
476 						XMP_StringPtr  itemValue,
477 						XMP_OptionBits options )
478 {
479 	XMP_Assert ( (schemaNS != 0) && (arrayName != 0) );	// Enforced by wrapper.
480 
481 	XMP_ExpandedXPath arrayPath;
482 	ExpandXPath ( schemaNS, arrayName, &arrayPath );
483 	XMP_Node * arrayNode = FindNode ( &tree, arrayPath, kXMP_ExistingOnly );	// Just lookup, don't try to create.
484 	if ( arrayNode == 0 ) XMP_Throw ( "Specified array does not exist", kXMPErr_BadXPath );
485 
486 	DoSetArrayItem ( arrayNode, itemIndex, itemValue, options );
487 
488 }	// SetArrayItem
489 
490 
491 // -------------------------------------------------------------------------------------------------
492 // AppendArrayItem
493 // ---------------
494 
495 void
AppendArrayItem(XMP_StringPtr schemaNS,XMP_StringPtr arrayName,XMP_OptionBits arrayOptions,XMP_StringPtr itemValue,XMP_OptionBits options)496 XMPMeta::AppendArrayItem ( XMP_StringPtr  schemaNS,
497 						   XMP_StringPtr  arrayName,
498 						   XMP_OptionBits arrayOptions,
499 						   XMP_StringPtr  itemValue,
500 						   XMP_OptionBits options )
501 {
502 	XMP_Assert ( (schemaNS != 0) && (arrayName != 0) );	// Enforced by wrapper.
503 
504 	arrayOptions = VerifySetOptions ( arrayOptions, 0 );
505 	if ( (arrayOptions & ~kXMP_PropArrayFormMask) != 0 ) {
506 		XMP_Throw ( "Only array form flags allowed for arrayOptions", kXMPErr_BadOptions );
507 	}
508 
509 	// Locate or create the array. If it already exists, make sure the array form from the options
510 	// parameter is compatible with the current state.
511 
512 	XMP_ExpandedXPath arrayPath;
513 	ExpandXPath ( schemaNS, arrayName, &arrayPath );
514 	XMP_Node * arrayNode = FindNode ( &tree, arrayPath, kXMP_ExistingOnly );	// Just lookup, don't try to create.
515 
516 	if ( arrayNode != 0 ) {
517 		// The array exists, make sure the form is compatible. Zero arrayForm means take what exists.
518 		if ( ! (arrayNode->options & kXMP_PropValueIsArray) ) {
519 			XMP_Throw ( "The named property is not an array", kXMPErr_BadXPath );
520 		}
521 		#if 0
522 			// *** Disable for now. Need to do some general rethinking of semantic checks.
523 			if ( (arrayOptions != 0) && (arrayOptions != (arrayNode->options & kXMP_PropArrayFormMask)) ) {
524 				XMP_Throw ( "Mismatch of existing and specified array form", kXMPErr_BadOptions );
525 			}
526 		#endif
527 	} else {
528 		// The array does not exist, try to create it.
529 		if ( arrayOptions == 0 ) XMP_Throw ( "Explicit arrayOptions required to create new array", kXMPErr_BadOptions );
530 		arrayNode = FindNode ( &tree, arrayPath, kXMP_CreateNodes, arrayOptions );
531 		if ( arrayNode == 0 ) XMP_Throw ( "Failure creating array node", kXMPErr_BadXPath );
532 	}
533 
534 	DoSetArrayItem ( arrayNode, kXMP_ArrayLastItem, itemValue, (options | kXMP_InsertAfterItem) );
535 
536 }	// AppendArrayItem
537 
538 
539 // -------------------------------------------------------------------------------------------------
540 // SetStructField
541 // --------------
542 
543 void
SetStructField(XMP_StringPtr schemaNS,XMP_StringPtr structName,XMP_StringPtr fieldNS,XMP_StringPtr fieldName,XMP_StringPtr fieldValue,XMP_OptionBits options)544 XMPMeta::SetStructField	( XMP_StringPtr	 schemaNS,
545 						  XMP_StringPtr	 structName,
546 						  XMP_StringPtr	 fieldNS,
547 						  XMP_StringPtr	 fieldName,
548 						  XMP_StringPtr	 fieldValue,
549 						  XMP_OptionBits options )
550 {
551 	XMP_Assert ( (schemaNS != 0) && (structName != 0) && (fieldNS != 0) && (fieldName != 0) );	// Enforced by wrapper.
552 
553 	XMP_StringPtr	fieldPath;
554 	XMP_StringLen	pathLen;
555 
556 	XMPUtils::ComposeStructFieldPath ( schemaNS, structName, fieldNS, fieldName, &fieldPath, &pathLen );
557 	SetProperty ( schemaNS, fieldPath, fieldValue, options );
558 
559 }	// SetStructField
560 
561 
562 // -------------------------------------------------------------------------------------------------
563 // SetQualifier
564 // ------------
565 
566 void
SetQualifier(XMP_StringPtr schemaNS,XMP_StringPtr propName,XMP_StringPtr qualNS,XMP_StringPtr qualName,XMP_StringPtr qualValue,XMP_OptionBits options)567 XMPMeta::SetQualifier ( XMP_StringPtr  schemaNS,
568 						XMP_StringPtr  propName,
569 						XMP_StringPtr  qualNS,
570 						XMP_StringPtr  qualName,
571 						XMP_StringPtr  qualValue,
572 						XMP_OptionBits options )
573 {
574 	XMP_Assert ( (schemaNS != 0) && (propName != 0) && (qualNS != 0) && (qualName != 0) );	// Enforced by wrapper.
575 
576 	XMP_StringPtr	qualPath;
577 	XMP_StringLen	pathLen;
578 
579 	XMP_ExpandedXPath expPath;
580 	ExpandXPath ( schemaNS, propName, &expPath );
581 	XMP_Node * propNode = FindNode ( &tree, expPath, kXMP_ExistingOnly );
582 	if ( propNode == 0 ) XMP_Throw ( "Specified property does not exist", kXMPErr_BadXPath );
583 
584 	XMPUtils::ComposeQualifierPath ( schemaNS, propName, qualNS, qualName, &qualPath, &pathLen );
585 	SetProperty ( schemaNS, qualPath, qualValue, options );
586 
587 }	// SetQualifier
588 
589 
590 // -------------------------------------------------------------------------------------------------
591 // DeleteProperty
592 // --------------
593 
594 void
DeleteProperty(XMP_StringPtr schemaNS,XMP_StringPtr propName)595 XMPMeta::DeleteProperty	( XMP_StringPtr	schemaNS,
596 						  XMP_StringPtr	propName )
597 {
598 	XMP_Assert ( (schemaNS != 0) && (propName != 0) );	// Enforced by wrapper.
599 
600 	XMP_ExpandedXPath	expPath;
601 	ExpandXPath ( schemaNS, propName, &expPath );
602 
603 	XMP_NodePtrPos ptrPos;
604 	XMP_Node * propNode = FindNode ( &tree, expPath, kXMP_ExistingOnly, kXMP_NoOptions, &ptrPos );
605 	if ( propNode == 0 ) return;
606 	XMP_Node * parentNode = propNode->parent;
607 
608 	// Erase the pointer from the parent's vector, then delete the node and all below it.
609 
610 	if ( ! (propNode->options & kXMP_PropIsQualifier) ) {
611 
612 		parentNode->children.erase ( ptrPos );
613 		DeleteEmptySchema ( parentNode );
614 
615 	} else {
616 
617 		if ( propNode->name == "xml:lang" ) {
618 			XMP_Assert ( parentNode->options & kXMP_PropHasLang );	// *** &= ~flag would be safer
619 			parentNode->options ^= kXMP_PropHasLang;
620 		} else if ( propNode->name == "rdf:type" ) {
621 			XMP_Assert ( parentNode->options & kXMP_PropHasType );
622 			parentNode->options ^= kXMP_PropHasType;
623 		}
624 
625 		parentNode->qualifiers.erase ( ptrPos );
626 		XMP_Assert ( parentNode->options & kXMP_PropHasQualifiers );
627 		if ( parentNode->qualifiers.empty() ) parentNode->options ^= kXMP_PropHasQualifiers;
628 
629 	}
630 
631 	delete propNode;	// ! The destructor takes care of the whole subtree.
632 
633 }	// DeleteProperty
634 
635 
636 // -------------------------------------------------------------------------------------------------
637 // DeleteArrayItem
638 // ---------------
639 
640 void
DeleteArrayItem(XMP_StringPtr schemaNS,XMP_StringPtr arrayName,XMP_Index itemIndex)641 XMPMeta::DeleteArrayItem ( XMP_StringPtr schemaNS,
642 						   XMP_StringPtr arrayName,
643 						   XMP_Index	 itemIndex )
644 {
645 	XMP_Assert ( (schemaNS != 0) && (arrayName != 0) );	// Enforced by wrapper.
646 
647 	XMP_StringPtr	itemPath;
648 	XMP_StringLen	pathLen;
649 
650 	XMPUtils::ComposeArrayItemPath ( schemaNS, arrayName, itemIndex, &itemPath, &pathLen );
651 	DeleteProperty ( schemaNS, itemPath );
652 
653 }	// DeleteArrayItem
654 
655 
656 // -------------------------------------------------------------------------------------------------
657 // DeleteStructField
658 // -----------------
659 
660 void
DeleteStructField(XMP_StringPtr schemaNS,XMP_StringPtr structName,XMP_StringPtr fieldNS,XMP_StringPtr fieldName)661 XMPMeta::DeleteStructField ( XMP_StringPtr schemaNS,
662 							 XMP_StringPtr structName,
663 							 XMP_StringPtr fieldNS,
664 							 XMP_StringPtr fieldName )
665 {
666 	XMP_Assert ( (schemaNS != 0) && (structName != 0) && (fieldNS != 0) && (fieldName != 0) );	// Enforced by wrapper.
667 
668 	XMP_StringPtr	fieldPath;
669 	XMP_StringLen	pathLen;
670 
671 	XMPUtils::ComposeStructFieldPath ( schemaNS, structName, fieldNS, fieldName, &fieldPath, &pathLen );
672 	DeleteProperty ( schemaNS, fieldPath );
673 
674 }	// DeleteStructField
675 
676 
677 // -------------------------------------------------------------------------------------------------
678 // DeleteQualifier
679 // ---------------
680 
681 void
DeleteQualifier(XMP_StringPtr schemaNS,XMP_StringPtr propName,XMP_StringPtr qualNS,XMP_StringPtr qualName)682 XMPMeta::DeleteQualifier ( XMP_StringPtr schemaNS,
683 						   XMP_StringPtr propName,
684 						   XMP_StringPtr qualNS,
685 						   XMP_StringPtr qualName )
686 {
687 	XMP_Assert ( (schemaNS != 0) && (propName != 0) && (qualNS != 0) && (qualName != 0) );	// Enforced by wrapper.
688 
689 	XMP_StringPtr	qualPath;
690 	XMP_StringLen	pathLen;
691 
692 	XMPUtils::ComposeQualifierPath ( schemaNS, propName, qualNS, qualName, &qualPath, &pathLen );
693 	DeleteProperty ( schemaNS, qualPath );
694 
695 }	// DeleteQualifier
696 
697 
698 // -------------------------------------------------------------------------------------------------
699 // DoesPropertyExist
700 // -----------------
701 
702 bool
DoesPropertyExist(XMP_StringPtr schemaNS,XMP_StringPtr propName) const703 XMPMeta::DoesPropertyExist ( XMP_StringPtr schemaNS,
704 							 XMP_StringPtr propName ) const
705 {
706 	XMP_Assert ( (schemaNS != 0) && (propName != 0) );	// Enforced by wrapper.
707 
708 	XMP_ExpandedXPath	expPath;
709 	ExpandXPath ( schemaNS, propName, &expPath );
710 
711 	XMP_Node * propNode = FindConstNode ( &tree, expPath );
712 	return (propNode != 0);
713 
714 }	// DoesPropertyExist
715 
716 
717 // -------------------------------------------------------------------------------------------------
718 // DoesArrayItemExist
719 // ------------------
720 
721 bool
DoesArrayItemExist(XMP_StringPtr schemaNS,XMP_StringPtr arrayName,XMP_Index itemIndex) const722 XMPMeta::DoesArrayItemExist	( XMP_StringPtr	schemaNS,
723 							  XMP_StringPtr	arrayName,
724 							  XMP_Index		itemIndex ) const
725 {
726 	XMP_Assert ( (schemaNS != 0) && (arrayName != 0) );	// Enforced by wrapper.
727 
728 	XMP_StringPtr	itemPath;
729 	XMP_StringLen	pathLen;
730 
731 	XMPUtils::ComposeArrayItemPath ( schemaNS, arrayName, itemIndex, &itemPath, &pathLen );
732 	return DoesPropertyExist ( schemaNS, itemPath );
733 
734 }	// DoesArrayItemExist
735 
736 
737 // -------------------------------------------------------------------------------------------------
738 // DoesStructFieldExist
739 // --------------------
740 
741 bool
DoesStructFieldExist(XMP_StringPtr schemaNS,XMP_StringPtr structName,XMP_StringPtr fieldNS,XMP_StringPtr fieldName) const742 XMPMeta::DoesStructFieldExist ( XMP_StringPtr schemaNS,
743 								XMP_StringPtr structName,
744 								XMP_StringPtr fieldNS,
745 								XMP_StringPtr fieldName ) const
746 {
747 	XMP_Assert ( (schemaNS != 0) && (structName != 0) && (fieldNS != 0) && (fieldName != 0) );	// Enforced by wrapper.
748 
749 	XMP_StringPtr	fieldPath;
750 	XMP_StringLen	pathLen;
751 
752 	XMPUtils::ComposeStructFieldPath ( schemaNS, structName, fieldNS, fieldName, &fieldPath, &pathLen );
753 	return DoesPropertyExist ( schemaNS, fieldPath );
754 
755 }	// DoesStructFieldExist
756 
757 
758 // -------------------------------------------------------------------------------------------------
759 // DoesQualifierExist
760 // ------------------
761 
762 bool
DoesQualifierExist(XMP_StringPtr schemaNS,XMP_StringPtr propName,XMP_StringPtr qualNS,XMP_StringPtr qualName) const763 XMPMeta::DoesQualifierExist	( XMP_StringPtr	schemaNS,
764 							  XMP_StringPtr	propName,
765 							  XMP_StringPtr	qualNS,
766 							  XMP_StringPtr	qualName ) const
767 {
768 	XMP_Assert ( (schemaNS != 0) && (propName != 0) && (qualNS != 0) && (qualName != 0) );	// Enforced by wrapper.
769 
770 	XMP_StringPtr	qualPath;
771 	XMP_StringLen	pathLen;
772 
773 	XMPUtils::ComposeQualifierPath ( schemaNS, propName, qualNS, qualName, &qualPath, &pathLen );
774 	return DoesPropertyExist ( schemaNS, qualPath );
775 
776 }	// DoesQualifierExist
777 
778 
779 // -------------------------------------------------------------------------------------------------
780 // GetLocalizedText
781 // ----------------
782 
783 bool
GetLocalizedText(XMP_StringPtr schemaNS,XMP_StringPtr arrayName,XMP_StringPtr _genericLang,XMP_StringPtr _specificLang,XMP_StringPtr * actualLang,XMP_StringLen * langSize,XMP_StringPtr * itemValue,XMP_StringLen * valueSize,XMP_OptionBits * options) const784 XMPMeta::GetLocalizedText ( XMP_StringPtr	 schemaNS,
785 							XMP_StringPtr	 arrayName,
786 							XMP_StringPtr	 _genericLang,
787 							XMP_StringPtr	 _specificLang,
788 							XMP_StringPtr *	 actualLang,
789 							XMP_StringLen *	 langSize,
790 							XMP_StringPtr *	 itemValue,
791 							XMP_StringLen *	 valueSize,
792 							XMP_OptionBits * options ) const
793 {
794 	XMP_Assert ( (schemaNS != 0) && (arrayName != 0) && (_genericLang != 0) && (_specificLang != 0) );	// Enforced by wrapper.
795 	XMP_Assert ( (actualLang != 0) && (langSize != 0) );	// Enforced by wrapper.
796 	XMP_Assert ( (itemValue != 0) && (valueSize != 0) && (options != 0) );	// Enforced by wrapper.
797 
798 	XMP_VarString zGenericLang  ( _genericLang );
799 	XMP_VarString zSpecificLang ( _specificLang );
800 	NormalizeLangValue ( &zGenericLang );
801 	NormalizeLangValue ( &zSpecificLang );
802 
803 	XMP_StringPtr genericLang  = zGenericLang.c_str();
804 	XMP_StringPtr specificLang = zSpecificLang.c_str();
805 
806 	XMP_ExpandedXPath arrayPath;
807 	ExpandXPath ( schemaNS, arrayName, &arrayPath );
808 
809 	const XMP_Node * arrayNode = FindConstNode ( &tree, arrayPath );	// *** This expand/find idiom is used in 3 Getters.
810 	if ( arrayNode == 0 ) return false;			// *** Should extract it into a local utility.
811 
812 	XMP_CLTMatch match;
813 	const XMP_Node * itemNode;
814 
815 	match = ChooseLocalizedText ( arrayNode, genericLang, specificLang, &itemNode );
816 	if ( match == kXMP_CLT_NoValues ) return false;
817 
818 	*actualLang = itemNode->qualifiers[0]->value.c_str();
819 	*langSize   = itemNode->qualifiers[0]->value.size();
820 	*itemValue  = itemNode->value.c_str();
821 	*valueSize  = itemNode->value.size();
822 	*options    = itemNode->options;
823 
824 	return true;
825 
826 }	// GetLocalizedText
827 
828 
829 // -------------------------------------------------------------------------------------------------
830 // SetLocalizedText
831 // ----------------
832 
833 void
SetLocalizedText(XMP_StringPtr schemaNS,XMP_StringPtr arrayName,XMP_StringPtr _genericLang,XMP_StringPtr _specificLang,XMP_StringPtr itemValue,XMP_OptionBits options)834 XMPMeta::SetLocalizedText ( XMP_StringPtr  schemaNS,
835 							XMP_StringPtr  arrayName,
836 							XMP_StringPtr  _genericLang,
837 							XMP_StringPtr  _specificLang,
838 							XMP_StringPtr  itemValue,
839 							XMP_OptionBits options )
840 {
841 	options = options;	// Avoid unused parameter warning.
842 
843 	XMP_Assert ( (schemaNS != 0) && (arrayName != 0) && (_genericLang != 0) && (_specificLang != 0) );	// Enforced by wrapper.
844 
845 	XMP_VarString zGenericLang  ( _genericLang );
846 	XMP_VarString zSpecificLang ( _specificLang );
847 	NormalizeLangValue ( &zGenericLang );
848 	NormalizeLangValue ( &zSpecificLang );
849 
850 	XMP_StringPtr genericLang  = zGenericLang.c_str();
851 	XMP_StringPtr specificLang = zSpecificLang.c_str();
852 
853 	XMP_ExpandedXPath arrayPath;
854 	ExpandXPath ( schemaNS, arrayName, &arrayPath );
855 
856 	// Find the array node and set the options if it was just created.
857 	XMP_Node * arrayNode = FindNode ( &tree, arrayPath, kXMP_CreateNodes,
858 									  (kXMP_PropValueIsArray | kXMP_PropArrayIsOrdered | kXMP_PropArrayIsAlternate) );
859 	if ( arrayNode == 0 ) XMP_Throw ( "Failed to find or create array node", kXMPErr_BadXPath );
860 	if ( ! XMP_ArrayIsAltText(arrayNode->options) ) {
861 		if ( arrayNode->children.empty() && XMP_ArrayIsAlternate(arrayNode->options) ) {
862 			arrayNode->options |= kXMP_PropArrayIsAltText;
863 		} else {
864 			XMP_Throw ( "Localized text array is not alt-text", kXMPErr_BadXPath );
865 		}
866 	}
867 
868 	// Make sure the x-default item, if any, is first.
869 
870 	size_t itemNum, itemLim;
871 	XMP_Node * xdItem = 0;
872 	bool haveXDefault = false;
873 
874 	for ( itemNum = 0, itemLim = arrayNode->children.size(); itemNum < itemLim; ++itemNum ) {
875 		XMP_Node * currItem = arrayNode->children[itemNum];
876 		XMP_Assert ( XMP_PropHasLang(currItem->options) );
877 		if ( currItem->qualifiers.empty() || (currItem->qualifiers[0]->name != "xml:lang") ) {
878 			XMP_Throw ( "Language qualifier must be first", kXMPErr_BadXPath );
879 		}
880 		if ( currItem->qualifiers[0]->value == "x-default" ) {
881 			xdItem = currItem;
882 			haveXDefault = true;
883 			break;
884 		}
885 	}
886 
887 	if ( haveXDefault && (itemNum != 0) ) {
888 		XMP_Assert ( arrayNode->children[itemNum]->qualifiers[0]->value == "x-default" );
889 		XMP_Node * temp = arrayNode->children[0];
890 		arrayNode->children[0] = arrayNode->children[itemNum];
891 		arrayNode->children[itemNum] = temp;
892 	}
893 
894 	// Find the appropriate item. ChooseLocalizedText will make sure the array is a language alternative.
895 
896 	const XMP_Node * cItemNode;	// ! ChooseLocalizedText returns a pointer to a const node.
897 	XMP_CLTMatch match = ChooseLocalizedText ( arrayNode, genericLang, specificLang, &cItemNode );
898 	XMP_Node * itemNode = const_cast<XMP_Node*> ( cItemNode );
899 
900 	const bool specificXDefault = XMP_LitMatch ( specificLang, "x-default" );
901 
902 	switch ( match ) {
903 
904 		case kXMP_CLT_NoValues :
905 
906 			// Create the array items for the specificLang and x-default, with x-default first.
907 			AppendLangItem ( arrayNode, "x-default", itemValue );
908 			haveXDefault = true;
909 			if ( ! specificXDefault ) AppendLangItem ( arrayNode, specificLang, itemValue );
910 			break;
911 
912 		case kXMP_CLT_SpecificMatch :
913 
914 			if ( ! specificXDefault ) {
915 				// Update the specific item, update x-default if it matches the old value.
916 				if ( haveXDefault && (xdItem != itemNode) && (xdItem->value == itemNode->value) ) {
917 					SetNodeValue ( xdItem, itemValue );
918 				}
919 				SetNodeValue ( itemNode, itemValue );	// ! Do this after the x-default check!
920 			} else {
921 				// Update all items whose values match the old x-default value.
922 				XMP_Assert ( haveXDefault && (xdItem == itemNode) );
923 				for ( itemNum = 0, itemLim = arrayNode->children.size(); itemNum < itemLim; ++itemNum ) {
924 					XMP_Node * currItem = arrayNode->children[itemNum];
925 					if ( (currItem == xdItem) || (currItem->value != xdItem->value) ) continue;
926 					SetNodeValue ( currItem, itemValue );
927 				}
928 				SetNodeValue ( xdItem, itemValue );	// And finally do the x-default item.
929 			}
930 			break;
931 
932 		case kXMP_CLT_SingleGeneric :
933 
934 			// Update the generic item, update x-default if it matches the old value.
935 			if ( haveXDefault && (xdItem != itemNode) && (xdItem->value == itemNode->value) ) {
936 				SetNodeValue ( xdItem, itemValue );
937 			}
938 			SetNodeValue ( itemNode, itemValue );	// ! Do this after the x-default check!
939 			break;
940 
941 		case kXMP_CLT_MultipleGeneric :
942 
943 			// Create the specific language, ignore x-default.
944 			AppendLangItem ( arrayNode, specificLang, itemValue );
945 			if ( specificXDefault ) haveXDefault = true;
946 			break;
947 
948 		case kXMP_CLT_XDefault :
949 
950 			// Create the specific language, update x-default if it was the only item.
951 			if ( arrayNode->children.size() == 1 ) SetNodeValue ( xdItem, itemValue );
952 			AppendLangItem ( arrayNode, specificLang, itemValue );
953 			break;
954 
955 		case kXMP_CLT_FirstItem	:
956 
957 			// Create the specific language, don't add an x-default item.
958 			AppendLangItem ( arrayNode, specificLang, itemValue );
959 			if ( specificXDefault ) haveXDefault = true;
960 			break;
961 
962 		default :
963 			XMP_Throw ( "Unexpected result from ChooseLocalizedText", kXMPErr_InternalFailure );
964 
965 	}
966 
967 	// Add an x-default at the front if needed.
968 	if ( (! haveXDefault) && (arrayNode->children.size() == 1) ) {
969 		AppendLangItem ( arrayNode, "x-default", itemValue );
970 	}
971 
972 }	// SetLocalizedText
973 
974 
975 // -------------------------------------------------------------------------------------------------
976 // GetProperty_Bool
977 // ----------------
978 
979 bool
GetProperty_Bool(XMP_StringPtr schemaNS,XMP_StringPtr propName,bool * propValue,XMP_OptionBits * options) const980 XMPMeta::GetProperty_Bool ( XMP_StringPtr	 schemaNS,
981 							XMP_StringPtr	 propName,
982 							bool *			 propValue,
983 							XMP_OptionBits * options ) const
984 {
985 	XMP_Assert ( (schemaNS != 0) && (propName != 0) );	// Enforced by wrapper.
986 	XMP_Assert ( (propValue != 0) && (options != 0) );	// Enforced by wrapper.
987 
988 	XMP_StringPtr	valueStr;
989 	XMP_StringLen	valueLen;
990 
991 	bool found = GetProperty ( schemaNS, propName, &valueStr, &valueLen, options );
992 	if ( found ) {
993 		if ( ! XMP_PropIsSimple ( *options ) ) XMP_Throw ( "Property must be simple", kXMPErr_BadXPath );
994 		*propValue = XMPUtils::ConvertToBool ( valueStr );
995 	}
996 	return found;
997 
998 }	// GetProperty_Bool
999 
1000 
1001 // -------------------------------------------------------------------------------------------------
1002 // GetProperty_Int
1003 // ---------------
1004 
1005 bool
GetProperty_Int(XMP_StringPtr schemaNS,XMP_StringPtr propName,XMP_Int32 * propValue,XMP_OptionBits * options) const1006 XMPMeta::GetProperty_Int ( XMP_StringPtr	schemaNS,
1007 						   XMP_StringPtr	propName,
1008 						   XMP_Int32 *		propValue,
1009 						   XMP_OptionBits *	options ) const
1010 {
1011 	XMP_Assert ( (schemaNS != 0) && (propName != 0) );	// Enforced by wrapper.
1012 	XMP_Assert ( (propValue != 0) && (options != 0) );	// Enforced by wrapper.
1013 
1014 	XMP_StringPtr	valueStr;
1015 	XMP_StringLen	valueLen;
1016 
1017 	bool found = GetProperty ( schemaNS, propName, &valueStr, &valueLen, options );
1018 	if ( found ) {
1019 		if ( ! XMP_PropIsSimple ( *options ) ) XMP_Throw ( "Property must be simple", kXMPErr_BadXPath );
1020 		*propValue = XMPUtils::ConvertToInt ( valueStr );
1021 	}
1022 	return found;
1023 
1024 }	// GetProperty_Int
1025 
1026 
1027 // -------------------------------------------------------------------------------------------------
1028 // GetProperty_Int64
1029 // -----------------
1030 
1031 bool
GetProperty_Int64(XMP_StringPtr schemaNS,XMP_StringPtr propName,XMP_Int64 * propValue,XMP_OptionBits * options) const1032 XMPMeta::GetProperty_Int64 ( XMP_StringPtr	  schemaNS,
1033 						     XMP_StringPtr	  propName,
1034 						     XMP_Int64 *	  propValue,
1035 						     XMP_OptionBits * options ) const
1036 {
1037 	XMP_Assert ( (schemaNS != 0) && (propName != 0) );	// Enforced by wrapper.
1038 	XMP_Assert ( (propValue != 0) && (options != 0) );	// Enforced by wrapper.
1039 
1040 	XMP_StringPtr	valueStr;
1041 	XMP_StringLen	valueLen;
1042 
1043 	bool found = GetProperty ( schemaNS, propName, &valueStr, &valueLen, options );
1044 	if ( found ) {
1045 		if ( ! XMP_PropIsSimple ( *options ) ) XMP_Throw ( "Property must be simple", kXMPErr_BadXPath );
1046 		*propValue = XMPUtils::ConvertToInt64 ( valueStr );
1047 	}
1048 	return found;
1049 
1050 }	// GetProperty_Int64
1051 
1052 
1053 // -------------------------------------------------------------------------------------------------
1054 // GetProperty_Float
1055 // -----------------
1056 
1057 bool
GetProperty_Float(XMP_StringPtr schemaNS,XMP_StringPtr propName,double * propValue,XMP_OptionBits * options) const1058 XMPMeta::GetProperty_Float ( XMP_StringPtr	  schemaNS,
1059 							 XMP_StringPtr	  propName,
1060 							 double *		  propValue,
1061 							 XMP_OptionBits * options ) const
1062 {
1063 	XMP_Assert ( (schemaNS != 0) && (propName != 0) );	// Enforced by wrapper.
1064 	XMP_Assert ( (propValue != 0) && (options != 0) );	// Enforced by wrapper.
1065 
1066 	XMP_StringPtr	valueStr;
1067 	XMP_StringLen	valueLen;
1068 
1069 	bool found = GetProperty ( schemaNS, propName, &valueStr, &valueLen, options );
1070 	if ( found ) {
1071 		if ( ! XMP_PropIsSimple ( *options ) ) XMP_Throw ( "Property must be simple", kXMPErr_BadXPath );
1072 		*propValue = XMPUtils::ConvertToFloat ( valueStr );
1073 	}
1074 	return found;
1075 
1076 }	// GetProperty_Float
1077 
1078 
1079 // -------------------------------------------------------------------------------------------------
1080 // GetProperty_Date
1081 // ----------------
1082 
1083 bool
GetProperty_Date(XMP_StringPtr schemaNS,XMP_StringPtr propName,XMP_DateTime * propValue,XMP_OptionBits * options) const1084 XMPMeta::GetProperty_Date ( XMP_StringPtr	 schemaNS,
1085 							XMP_StringPtr	 propName,
1086 							XMP_DateTime *	 propValue,
1087 							XMP_OptionBits * options ) const
1088 {
1089 	XMP_Assert ( (schemaNS != 0) && (propName != 0) );	// Enforced by wrapper.
1090 	XMP_Assert ( (propValue != 0) && (options != 0) );	// Enforced by wrapper.
1091 
1092 	XMP_StringPtr	valueStr;
1093 	XMP_StringLen	valueLen;
1094 
1095 	bool found = GetProperty ( schemaNS, propName, &valueStr, &valueLen, options );
1096 	if ( found )  {
1097 		if ( ! XMP_PropIsSimple ( *options ) ) XMP_Throw ( "Property must be simple", kXMPErr_BadXPath );
1098 		XMPUtils::ConvertToDate ( valueStr, propValue );
1099 	}
1100 	return found;
1101 
1102 }	// GetProperty_Date
1103 
1104 
1105 // -------------------------------------------------------------------------------------------------
1106 // SetProperty_Bool
1107 // ----------------
1108 
1109 void
SetProperty_Bool(XMP_StringPtr schemaNS,XMP_StringPtr propName,bool propValue,XMP_OptionBits options)1110 XMPMeta::SetProperty_Bool ( XMP_StringPtr  schemaNS,
1111 							XMP_StringPtr  propName,
1112 							bool		   propValue,
1113 							XMP_OptionBits options )
1114 {
1115 	XMP_Assert ( (schemaNS != 0) && (propName != 0) );	// Enforced by wrapper.
1116 
1117 	XMP_StringPtr	valueStr;
1118 	XMP_StringLen	valueLen;
1119 
1120 	XMPUtils::ConvertFromBool ( propValue, &valueStr, &valueLen );
1121 	SetProperty ( schemaNS, propName, valueStr, options );
1122 
1123 }	// SetProperty_Bool
1124 
1125 
1126 // -------------------------------------------------------------------------------------------------
1127 // SetProperty_Int
1128 // ---------------
1129 
1130 void
SetProperty_Int(XMP_StringPtr schemaNS,XMP_StringPtr propName,XMP_Int32 propValue,XMP_OptionBits options)1131 XMPMeta::SetProperty_Int ( XMP_StringPtr  schemaNS,
1132 						   XMP_StringPtr  propName,
1133 						   XMP_Int32	  propValue,
1134 						   XMP_OptionBits options )
1135 {
1136 	XMP_Assert ( (schemaNS != 0) && (propName != 0) );	// Enforced by wrapper.
1137 
1138 	XMP_StringPtr	valueStr;
1139 	XMP_StringLen	valueLen;
1140 
1141 	XMPUtils::ConvertFromInt ( propValue, "", &valueStr, &valueLen );
1142 	SetProperty ( schemaNS, propName, valueStr, options );
1143 
1144 }	// SetProperty_Int
1145 
1146 
1147 // -------------------------------------------------------------------------------------------------
1148 // SetProperty_Int64
1149 // -----------------
1150 
1151 void
SetProperty_Int64(XMP_StringPtr schemaNS,XMP_StringPtr propName,XMP_Int64 propValue,XMP_OptionBits options)1152 XMPMeta::SetProperty_Int64 ( XMP_StringPtr  schemaNS,
1153 						     XMP_StringPtr  propName,
1154 						     XMP_Int64	    propValue,
1155 						     XMP_OptionBits options )
1156 {
1157 	XMP_Assert ( (schemaNS != 0) && (propName != 0) );	// Enforced by wrapper.
1158 
1159 	XMP_StringPtr	valueStr;
1160 	XMP_StringLen	valueLen;
1161 
1162 	XMPUtils::ConvertFromInt64 ( propValue, "", &valueStr, &valueLen );
1163 	SetProperty ( schemaNS, propName, valueStr, options );
1164 
1165 }	// SetProperty_Int64
1166 
1167 
1168 // -------------------------------------------------------------------------------------------------
1169 // SetProperty_Float
1170 // -----------------
1171 
1172 void
SetProperty_Float(XMP_StringPtr schemaNS,XMP_StringPtr propName,double propValue,XMP_OptionBits options)1173 XMPMeta::SetProperty_Float ( XMP_StringPtr	schemaNS,
1174 							 XMP_StringPtr	propName,
1175 							 double			propValue,
1176 							 XMP_OptionBits	options )
1177 {
1178 	XMP_Assert ( (schemaNS != 0) && (propName != 0) );	// Enforced by wrapper.
1179 
1180 	XMP_StringPtr	valueStr;
1181 	XMP_StringLen	valueLen;
1182 
1183 	XMPUtils::ConvertFromFloat ( propValue, "", &valueStr, &valueLen );
1184 	SetProperty ( schemaNS, propName, valueStr, options );
1185 
1186 }	// SetProperty_Float
1187 
1188 
1189 // -------------------------------------------------------------------------------------------------
1190 // SetProperty_Date
1191 // ----------------
1192 
1193 void
SetProperty_Date(XMP_StringPtr schemaNS,XMP_StringPtr propName,const XMP_DateTime & propValue,XMP_OptionBits options)1194 XMPMeta::SetProperty_Date ( XMP_StringPtr		   schemaNS,
1195 							XMP_StringPtr		   propName,
1196 							const	XMP_DateTime & propValue,
1197 							XMP_OptionBits		   options )
1198 {
1199 	XMP_Assert ( (schemaNS != 0) && (propName != 0) );	// Enforced by wrapper.
1200 
1201 	XMP_StringPtr	valueStr;
1202 	XMP_StringLen	valueLen;
1203 
1204 	XMPUtils::ConvertFromDate ( propValue, &valueStr, &valueLen );
1205 	SetProperty ( schemaNS, propName, valueStr, options );
1206 
1207 }	// SetProperty_Date
1208 
1209 // =================================================================================================
1210 
1211