1
// =================================================================================================
2
// Copyright 2002-2007 Adobe Systems Incorporated
3
// All Rights Reserved.
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.
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
// =================================================================================================
12
#include "XMP_Environment.h" // ! This must be the first include!
13
#include "XMPCore_Impl.hpp"
15
#include "XMPMeta.hpp"
16
#include "XMPIterator.hpp"
17
#include "XMPUtils.hpp"
19
#include "XMP_Version.h"
20
#include "UnicodeInlines.incl_cpp"
21
#include "UnicodeConversions.hpp"
22
#include "ExpatAdapter.hpp"
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)
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
42
// =================================================================================================
43
// Local Types and Constants
44
// =========================
46
typedef unsigned char XMP_CLTMatch;
48
enum { // Values for XMP_CLTMatch.
50
kXMP_CLT_SpecificMatch,
51
kXMP_CLT_SingleGeneric,
52
kXMP_CLT_MultipleGeneric,
58
// =================================================================================================
63
// =================================================================================================
68
// -------------------------------------------------------------------------------------------------
73
SetNodeValue ( XMP_Node * node, XMP_StringPtr value )
76
#if XMP_DebugBuild // ! Hack to force an assert.
77
if ( (node->name == "xap:TestAssertNotify") && XMP_LitMatch ( value, "DoIt!" ) ) {
78
XMP_Assert ( node->name != "xap:TestAssertNotify" );
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 ) {
94
XMP_Assert ( (*chPtr == 0) || (*chPtr >= 0x80) );
95
if ( *chPtr != 0 ) (void) GetCodePoint ( (const XMP_Uns8 **) &chPtr ); // Throws for bad UTF-8.
98
if ( XMP_PropIsQualifier(node->options) && (node->name == "xml:lang") ) NormalizeLangValue ( &node->value );
100
#if 0 // *** XMP_DebugBuild
101
node->_valuePtr = node->value.c_str();
107
// -------------------------------------------------------------------------------------------------
111
// The internals for SetProperty and related calls, used after the node is found or created.
114
SetNode ( XMP_Node * node, XMP_StringPtr value, XMP_OptionBits options )
116
if ( options & kXMP_DeleteExisting ) {
117
XMP_ClearOption ( options, kXMP_DeleteExisting );
118
node->options = options;
120
node->RemoveChildren();
121
node->RemoveQualifiers();
124
node->options |= options; // Keep options set by FindNode when creating a new node.
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 );
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 );
142
node->RemoveChildren();
149
// -------------------------------------------------------------------------------------------------
154
DoSetArrayItem ( XMP_Node * arrayNode,
156
XMP_StringPtr itemValue,
157
XMP_OptionBits options )
159
XMP_OptionBits itemLoc = options & kXMP_PropArrayLocationMask;
160
XMP_Index arraySize = arrayNode->children.size();
162
options &= ~kXMP_PropArrayLocationMask;
163
options = VerifySetOptions ( options, itemValue );
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.
170
XMP_Node * itemNode = 0;
172
if ( itemIndex == kXMP_ArrayLastItem ) itemIndex = arraySize;
173
if ( (itemIndex == 0) && (itemLoc == kXMP_InsertAfterItem) ) {
175
itemLoc = kXMP_InsertBeforeItem;
177
if ( (itemIndex == arraySize) && (itemLoc == kXMP_InsertAfterItem) ) {
181
if ( (itemIndex == arraySize+1) && (itemLoc == kXMP_InsertBeforeItem) ) itemLoc = 0;
183
if ( itemIndex == arraySize+1 ) {
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 );
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];
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 );
204
SetNode ( itemNode, itemValue, options );
209
// -------------------------------------------------------------------------------------------------
210
// ChooseLocalizedText
211
// -------------------
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.
219
ChooseLocalizedText ( const XMP_Node * arrayNode,
220
XMP_StringPtr genericLang,
221
XMP_StringPtr specificLang,
222
const XMP_Node * * itemNode )
224
const XMP_Node * currItem = 0;
225
const size_t itemLim = arrayNode->children.size();
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.
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 );
235
if ( arrayNode->children.empty() ) {
237
return kXMP_CLT_NoValues;
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 );
245
if ( currItem->qualifiers.empty() || (currItem->qualifiers[0]->name != "xml:lang") ) {
246
XMP_Throw ( "Alt-text array item has no language qualifier", kXMPErr_BadXPath );
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;
259
if ( *genericLang != 0 ) {
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.
275
if ( itemNum < itemLim ) {
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.
288
return kXMP_CLT_SingleGeneric; // No second partial match was found.
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;
303
// Everything failed, choose the first item.
304
*itemNode = arrayNode->children[0];
305
return kXMP_CLT_FirstItem;
307
} // ChooseLocalizedText
310
// -------------------------------------------------------------------------------------------------
315
AppendLangItem ( XMP_Node * arrayNode, XMP_StringPtr itemLang, XMP_StringPtr itemValue )
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 );
321
if ( (arrayNode->children.empty()) || (langQual->value != "x-default") ) {
322
arrayNode->children.push_back ( newItem );
324
arrayNode->children.insert ( arrayNode->children.begin(), newItem );
330
// =================================================================================================
335
// =================================================================================================
338
// -------------------------------------------------------------------------------------------------
343
XMPMeta::GetProperty ( XMP_StringPtr schemaNS,
344
XMP_StringPtr propName,
345
XMP_StringPtr * propValue,
346
XMP_StringLen * valueSize,
347
XMP_OptionBits * options ) const
349
XMP_Assert ( (schemaNS != 0) && (propName != 0) ); // Enforced by wrapper.
350
XMP_Assert ( (propValue != 0) && (valueSize != 0) && (options != 0) ); // Enforced by wrapper.
352
XMP_ExpandedXPath expPath;
353
ExpandXPath ( schemaNS, propName, &expPath );
355
XMP_Node * propNode = FindConstNode ( &tree, expPath );
356
if ( propNode == 0 ) return false;
358
*propValue = propNode->value.c_str();
359
*valueSize = propNode->value.size();
360
*options = propNode->options;
367
// -------------------------------------------------------------------------------------------------
372
XMPMeta::GetArrayItem ( XMP_StringPtr schemaNS,
373
XMP_StringPtr arrayName,
375
XMP_StringPtr * itemValue,
376
XMP_StringLen * valueSize,
377
XMP_OptionBits * options ) const
379
XMP_Assert ( (schemaNS != 0) && (arrayName != 0) ); // Enforced by wrapper.
380
XMP_Assert ( (itemValue != 0) && (valueSize != 0) && (options != 0) ); // Enforced by wrapper.
382
XMP_StringPtr itemPath;
383
XMP_StringLen pathLen;
385
XMPUtils::ComposeArrayItemPath ( schemaNS, arrayName, itemIndex, &itemPath, &pathLen );
386
return GetProperty ( schemaNS, itemPath, itemValue, valueSize, options );
391
// -------------------------------------------------------------------------------------------------
396
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
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.
407
XMP_StringPtr fieldPath;
408
XMP_StringLen pathLen;
410
XMPUtils::ComposeStructFieldPath ( schemaNS, structName, fieldNS, fieldName, &fieldPath, &pathLen );
411
return GetProperty ( schemaNS, fieldPath, fieldValue, valueSize, options );
416
// -------------------------------------------------------------------------------------------------
421
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
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.
432
XMP_StringPtr qualPath;
433
XMP_StringLen pathLen;
435
XMPUtils::ComposeQualifierPath ( schemaNS, propName, qualNS, qualName, &qualPath, &pathLen );
436
return GetProperty ( schemaNS, qualPath, qualValue, valueSize, options );
441
// -------------------------------------------------------------------------------------------------
445
// *** Should handle array items specially, calling SetArrayItem.
448
XMPMeta::SetProperty ( XMP_StringPtr schemaNS,
449
XMP_StringPtr propName,
450
XMP_StringPtr propValue,
451
XMP_OptionBits options )
453
XMP_Assert ( (schemaNS != 0) && (propName != 0) ); // Enforced by wrapper.
455
options = VerifySetOptions ( options, propValue );
457
XMP_ExpandedXPath expPath;
458
ExpandXPath ( schemaNS, propName, &expPath );
460
XMP_Node * propNode = FindNode ( &tree, expPath, kXMP_CreateNodes, options );
461
if ( propNode == 0 ) XMP_Throw ( "Specified property does not exist", kXMPErr_BadXPath );
463
SetNode ( propNode, propValue, options );
468
// -------------------------------------------------------------------------------------------------
473
XMPMeta::SetArrayItem ( XMP_StringPtr schemaNS,
474
XMP_StringPtr arrayName,
476
XMP_StringPtr itemValue,
477
XMP_OptionBits options )
479
XMP_Assert ( (schemaNS != 0) && (arrayName != 0) ); // Enforced by wrapper.
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 );
486
DoSetArrayItem ( arrayNode, itemIndex, itemValue, options );
491
// -------------------------------------------------------------------------------------------------
496
XMPMeta::AppendArrayItem ( XMP_StringPtr schemaNS,
497
XMP_StringPtr arrayName,
498
XMP_OptionBits arrayOptions,
499
XMP_StringPtr itemValue,
500
XMP_OptionBits options )
502
XMP_Assert ( (schemaNS != 0) && (arrayName != 0) ); // Enforced by wrapper.
504
arrayOptions = VerifySetOptions ( arrayOptions, 0 );
505
if ( (arrayOptions & ~kXMP_PropArrayFormMask) != 0 ) {
506
XMP_Throw ( "Only array form flags allowed for arrayOptions", kXMPErr_BadOptions );
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.
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.
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 );
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 );
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 );
534
DoSetArrayItem ( arrayNode, kXMP_ArrayLastItem, itemValue, (options | kXMP_InsertAfterItem) );
539
// -------------------------------------------------------------------------------------------------
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 )
551
XMP_Assert ( (schemaNS != 0) && (structName != 0) && (fieldNS != 0) && (fieldName != 0) ); // Enforced by wrapper.
553
XMP_StringPtr fieldPath;
554
XMP_StringLen pathLen;
556
XMPUtils::ComposeStructFieldPath ( schemaNS, structName, fieldNS, fieldName, &fieldPath, &pathLen );
557
SetProperty ( schemaNS, fieldPath, fieldValue, options );
562
// -------------------------------------------------------------------------------------------------
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 )
574
XMP_Assert ( (schemaNS != 0) && (propName != 0) && (qualNS != 0) && (qualName != 0) ); // Enforced by wrapper.
576
XMP_StringPtr qualPath;
577
XMP_StringLen pathLen;
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 );
584
XMPUtils::ComposeQualifierPath ( schemaNS, propName, qualNS, qualName, &qualPath, &pathLen );
585
SetProperty ( schemaNS, qualPath, qualValue, options );
590
// -------------------------------------------------------------------------------------------------
595
XMPMeta::DeleteProperty ( XMP_StringPtr schemaNS,
596
XMP_StringPtr propName )
598
XMP_Assert ( (schemaNS != 0) && (propName != 0) ); // Enforced by wrapper.
600
XMP_ExpandedXPath expPath;
601
ExpandXPath ( schemaNS, propName, &expPath );
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;
608
// Erase the pointer from the parent's vector, then delete the node and all below it.
610
if ( ! (propNode->options & kXMP_PropIsQualifier) ) {
612
parentNode->children.erase ( ptrPos );
613
DeleteEmptySchema ( parentNode );
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;
625
parentNode->qualifiers.erase ( ptrPos );
626
XMP_Assert ( parentNode->options & kXMP_PropHasQualifiers );
627
if ( parentNode->qualifiers.empty() ) parentNode->options ^= kXMP_PropHasQualifiers;
631
delete propNode; // ! The destructor takes care of the whole subtree.
636
// -------------------------------------------------------------------------------------------------
641
XMPMeta::DeleteArrayItem ( XMP_StringPtr schemaNS,
642
XMP_StringPtr arrayName,
643
XMP_Index itemIndex )
645
XMP_Assert ( (schemaNS != 0) && (arrayName != 0) ); // Enforced by wrapper.
647
XMP_StringPtr itemPath;
648
XMP_StringLen pathLen;
650
XMPUtils::ComposeArrayItemPath ( schemaNS, arrayName, itemIndex, &itemPath, &pathLen );
651
DeleteProperty ( schemaNS, itemPath );
656
// -------------------------------------------------------------------------------------------------
661
XMPMeta::DeleteStructField ( XMP_StringPtr schemaNS,
662
XMP_StringPtr structName,
663
XMP_StringPtr fieldNS,
664
XMP_StringPtr fieldName )
666
XMP_Assert ( (schemaNS != 0) && (structName != 0) && (fieldNS != 0) && (fieldName != 0) ); // Enforced by wrapper.
668
XMP_StringPtr fieldPath;
669
XMP_StringLen pathLen;
671
XMPUtils::ComposeStructFieldPath ( schemaNS, structName, fieldNS, fieldName, &fieldPath, &pathLen );
672
DeleteProperty ( schemaNS, fieldPath );
674
} // DeleteStructField
677
// -------------------------------------------------------------------------------------------------
682
XMPMeta::DeleteQualifier ( XMP_StringPtr schemaNS,
683
XMP_StringPtr propName,
684
XMP_StringPtr qualNS,
685
XMP_StringPtr qualName )
687
XMP_Assert ( (schemaNS != 0) && (propName != 0) && (qualNS != 0) && (qualName != 0) ); // Enforced by wrapper.
689
XMP_StringPtr qualPath;
690
XMP_StringLen pathLen;
692
XMPUtils::ComposeQualifierPath ( schemaNS, propName, qualNS, qualName, &qualPath, &pathLen );
693
DeleteProperty ( schemaNS, qualPath );
698
// -------------------------------------------------------------------------------------------------
703
XMPMeta::DoesPropertyExist ( XMP_StringPtr schemaNS,
704
XMP_StringPtr propName ) const
706
XMP_Assert ( (schemaNS != 0) && (propName != 0) ); // Enforced by wrapper.
708
XMP_ExpandedXPath expPath;
709
ExpandXPath ( schemaNS, propName, &expPath );
711
XMP_Node * propNode = FindConstNode ( &tree, expPath );
712
return (propNode != 0);
714
} // DoesPropertyExist
717
// -------------------------------------------------------------------------------------------------
718
// DoesArrayItemExist
719
// ------------------
722
XMPMeta::DoesArrayItemExist ( XMP_StringPtr schemaNS,
723
XMP_StringPtr arrayName,
724
XMP_Index itemIndex ) const
726
XMP_Assert ( (schemaNS != 0) && (arrayName != 0) ); // Enforced by wrapper.
728
XMP_StringPtr itemPath;
729
XMP_StringLen pathLen;
731
XMPUtils::ComposeArrayItemPath ( schemaNS, arrayName, itemIndex, &itemPath, &pathLen );
732
return DoesPropertyExist ( schemaNS, itemPath );
734
} // DoesArrayItemExist
737
// -------------------------------------------------------------------------------------------------
738
// DoesStructFieldExist
739
// --------------------
742
XMPMeta::DoesStructFieldExist ( XMP_StringPtr schemaNS,
743
XMP_StringPtr structName,
744
XMP_StringPtr fieldNS,
745
XMP_StringPtr fieldName ) const
747
XMP_Assert ( (schemaNS != 0) && (structName != 0) && (fieldNS != 0) && (fieldName != 0) ); // Enforced by wrapper.
749
XMP_StringPtr fieldPath;
750
XMP_StringLen pathLen;
752
XMPUtils::ComposeStructFieldPath ( schemaNS, structName, fieldNS, fieldName, &fieldPath, &pathLen );
753
return DoesPropertyExist ( schemaNS, fieldPath );
755
} // DoesStructFieldExist
758
// -------------------------------------------------------------------------------------------------
759
// DoesQualifierExist
760
// ------------------
763
XMPMeta::DoesQualifierExist ( XMP_StringPtr schemaNS,
764
XMP_StringPtr propName,
765
XMP_StringPtr qualNS,
766
XMP_StringPtr qualName ) const
768
XMP_Assert ( (schemaNS != 0) && (propName != 0) && (qualNS != 0) && (qualName != 0) ); // Enforced by wrapper.
770
XMP_StringPtr qualPath;
771
XMP_StringLen pathLen;
773
XMPUtils::ComposeQualifierPath ( schemaNS, propName, qualNS, qualName, &qualPath, &pathLen );
774
return DoesPropertyExist ( schemaNS, qualPath );
776
} // DoesQualifierExist
779
// -------------------------------------------------------------------------------------------------
784
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
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.
798
XMP_VarString zGenericLang ( _genericLang );
799
XMP_VarString zSpecificLang ( _specificLang );
800
NormalizeLangValue ( &zGenericLang );
801
NormalizeLangValue ( &zSpecificLang );
803
XMP_StringPtr genericLang = zGenericLang.c_str();
804
XMP_StringPtr specificLang = zSpecificLang.c_str();
806
XMP_ExpandedXPath arrayPath;
807
ExpandXPath ( schemaNS, arrayName, &arrayPath );
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.
813
const XMP_Node * itemNode;
815
match = ChooseLocalizedText ( arrayNode, genericLang, specificLang, &itemNode );
816
if ( match == kXMP_CLT_NoValues ) return false;
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;
826
} // GetLocalizedText
829
// -------------------------------------------------------------------------------------------------
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 )
841
options = options; // Avoid unused parameter warning.
843
XMP_Assert ( (schemaNS != 0) && (arrayName != 0) && (_genericLang != 0) && (_specificLang != 0) ); // Enforced by wrapper.
845
XMP_VarString zGenericLang ( _genericLang );
846
XMP_VarString zSpecificLang ( _specificLang );
847
NormalizeLangValue ( &zGenericLang );
848
NormalizeLangValue ( &zSpecificLang );
850
XMP_StringPtr genericLang = zGenericLang.c_str();
851
XMP_StringPtr specificLang = zSpecificLang.c_str();
853
XMP_ExpandedXPath arrayPath;
854
ExpandXPath ( schemaNS, arrayName, &arrayPath );
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;
864
XMP_Throw ( "Localized text array is not alt-text", kXMPErr_BadXPath );
868
// Make sure the x-default item, if any, is first.
870
size_t itemNum, itemLim;
871
XMP_Node * xdItem = 0;
872
bool haveXDefault = false;
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 );
880
if ( currItem->qualifiers[0]->value == "x-default" ) {
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;
894
// Find the appropriate item. ChooseLocalizedText will make sure the array is a language alternative.
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 );
900
const bool specificXDefault = XMP_LitMatch ( specificLang, "x-default" );
904
case kXMP_CLT_NoValues :
906
// Create the array items for the specificLang and x-default, with x-default first.
907
AppendLangItem ( arrayNode, "x-default", itemValue );
909
if ( ! specificXDefault ) AppendLangItem ( arrayNode, specificLang, itemValue );
912
case kXMP_CLT_SpecificMatch :
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 );
919
SetNodeValue ( itemNode, itemValue ); // ! Do this after the x-default check!
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 );
928
SetNodeValue ( xdItem, itemValue ); // And finally do the x-default item.
932
case kXMP_CLT_SingleGeneric :
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 );
938
SetNodeValue ( itemNode, itemValue ); // ! Do this after the x-default check!
941
case kXMP_CLT_MultipleGeneric :
943
// Create the specific language, ignore x-default.
944
AppendLangItem ( arrayNode, specificLang, itemValue );
945
if ( specificXDefault ) haveXDefault = true;
948
case kXMP_CLT_XDefault :
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 );
955
case kXMP_CLT_FirstItem :
957
// Create the specific language, don't add an x-default item.
958
AppendLangItem ( arrayNode, specificLang, itemValue );
959
if ( specificXDefault ) haveXDefault = true;
963
XMP_Throw ( "Unexpected result from ChooseLocalizedText", kXMPErr_InternalFailure );
967
// Add an x-default at the front if needed.
968
if ( (! haveXDefault) && (arrayNode->children.size() == 1) ) {
969
AppendLangItem ( arrayNode, "x-default", itemValue );
972
} // SetLocalizedText
975
// -------------------------------------------------------------------------------------------------
980
XMPMeta::GetProperty_Bool ( XMP_StringPtr schemaNS,
981
XMP_StringPtr propName,
983
XMP_OptionBits * options ) const
985
XMP_Assert ( (schemaNS != 0) && (propName != 0) ); // Enforced by wrapper.
986
XMP_Assert ( (propValue != 0) && (options != 0) ); // Enforced by wrapper.
988
XMP_StringPtr valueStr;
989
XMP_StringLen valueLen;
991
bool found = GetProperty ( schemaNS, propName, &valueStr, &valueLen, options );
993
if ( ! XMP_PropIsSimple ( *options ) ) XMP_Throw ( "Property must be simple", kXMPErr_BadXPath );
994
*propValue = XMPUtils::ConvertToBool ( valueStr );
998
} // GetProperty_Bool
1001
// -------------------------------------------------------------------------------------------------
1006
XMPMeta::GetProperty_Int ( XMP_StringPtr schemaNS,
1007
XMP_StringPtr propName,
1008
XMP_Int32 * propValue,
1009
XMP_OptionBits * options ) const
1011
XMP_Assert ( (schemaNS != 0) && (propName != 0) ); // Enforced by wrapper.
1012
XMP_Assert ( (propValue != 0) && (options != 0) ); // Enforced by wrapper.
1014
XMP_StringPtr valueStr;
1015
XMP_StringLen valueLen;
1017
bool found = GetProperty ( schemaNS, propName, &valueStr, &valueLen, options );
1019
if ( ! XMP_PropIsSimple ( *options ) ) XMP_Throw ( "Property must be simple", kXMPErr_BadXPath );
1020
*propValue = XMPUtils::ConvertToInt ( valueStr );
1024
} // GetProperty_Int
1027
// -------------------------------------------------------------------------------------------------
1028
// GetProperty_Int64
1029
// -----------------
1032
XMPMeta::GetProperty_Int64 ( XMP_StringPtr schemaNS,
1033
XMP_StringPtr propName,
1034
XMP_Int64 * propValue,
1035
XMP_OptionBits * options ) const
1037
XMP_Assert ( (schemaNS != 0) && (propName != 0) ); // Enforced by wrapper.
1038
XMP_Assert ( (propValue != 0) && (options != 0) ); // Enforced by wrapper.
1040
XMP_StringPtr valueStr;
1041
XMP_StringLen valueLen;
1043
bool found = GetProperty ( schemaNS, propName, &valueStr, &valueLen, options );
1045
if ( ! XMP_PropIsSimple ( *options ) ) XMP_Throw ( "Property must be simple", kXMPErr_BadXPath );
1046
*propValue = XMPUtils::ConvertToInt64 ( valueStr );
1050
} // GetProperty_Int64
1053
// -------------------------------------------------------------------------------------------------
1054
// GetProperty_Float
1055
// -----------------
1058
XMPMeta::GetProperty_Float ( XMP_StringPtr schemaNS,
1059
XMP_StringPtr propName,
1061
XMP_OptionBits * options ) const
1063
XMP_Assert ( (schemaNS != 0) && (propName != 0) ); // Enforced by wrapper.
1064
XMP_Assert ( (propValue != 0) && (options != 0) ); // Enforced by wrapper.
1066
XMP_StringPtr valueStr;
1067
XMP_StringLen valueLen;
1069
bool found = GetProperty ( schemaNS, propName, &valueStr, &valueLen, options );
1071
if ( ! XMP_PropIsSimple ( *options ) ) XMP_Throw ( "Property must be simple", kXMPErr_BadXPath );
1072
*propValue = XMPUtils::ConvertToFloat ( valueStr );
1076
} // GetProperty_Float
1079
// -------------------------------------------------------------------------------------------------
1084
XMPMeta::GetProperty_Date ( XMP_StringPtr schemaNS,
1085
XMP_StringPtr propName,
1086
XMP_DateTime * propValue,
1087
XMP_OptionBits * options ) const
1089
XMP_Assert ( (schemaNS != 0) && (propName != 0) ); // Enforced by wrapper.
1090
XMP_Assert ( (propValue != 0) && (options != 0) ); // Enforced by wrapper.
1092
XMP_StringPtr valueStr;
1093
XMP_StringLen valueLen;
1095
bool found = GetProperty ( schemaNS, propName, &valueStr, &valueLen, options );
1097
if ( ! XMP_PropIsSimple ( *options ) ) XMP_Throw ( "Property must be simple", kXMPErr_BadXPath );
1098
XMPUtils::ConvertToDate ( valueStr, propValue );
1102
} // GetProperty_Date
1105
// -------------------------------------------------------------------------------------------------
1110
XMPMeta::SetProperty_Bool ( XMP_StringPtr schemaNS,
1111
XMP_StringPtr propName,
1113
XMP_OptionBits options )
1115
XMP_Assert ( (schemaNS != 0) && (propName != 0) ); // Enforced by wrapper.
1117
XMP_StringPtr valueStr;
1118
XMP_StringLen valueLen;
1120
XMPUtils::ConvertFromBool ( propValue, &valueStr, &valueLen );
1121
SetProperty ( schemaNS, propName, valueStr, options );
1123
} // SetProperty_Bool
1126
// -------------------------------------------------------------------------------------------------
1131
XMPMeta::SetProperty_Int ( XMP_StringPtr schemaNS,
1132
XMP_StringPtr propName,
1133
XMP_Int32 propValue,
1134
XMP_OptionBits options )
1136
XMP_Assert ( (schemaNS != 0) && (propName != 0) ); // Enforced by wrapper.
1138
XMP_StringPtr valueStr;
1139
XMP_StringLen valueLen;
1141
XMPUtils::ConvertFromInt ( propValue, "", &valueStr, &valueLen );
1142
SetProperty ( schemaNS, propName, valueStr, options );
1144
} // SetProperty_Int
1147
// -------------------------------------------------------------------------------------------------
1148
// SetProperty_Int64
1149
// -----------------
1152
XMPMeta::SetProperty_Int64 ( XMP_StringPtr schemaNS,
1153
XMP_StringPtr propName,
1154
XMP_Int64 propValue,
1155
XMP_OptionBits options )
1157
XMP_Assert ( (schemaNS != 0) && (propName != 0) ); // Enforced by wrapper.
1159
XMP_StringPtr valueStr;
1160
XMP_StringLen valueLen;
1162
XMPUtils::ConvertFromInt64 ( propValue, "", &valueStr, &valueLen );
1163
SetProperty ( schemaNS, propName, valueStr, options );
1165
} // SetProperty_Int64
1168
// -------------------------------------------------------------------------------------------------
1169
// SetProperty_Float
1170
// -----------------
1173
XMPMeta::SetProperty_Float ( XMP_StringPtr schemaNS,
1174
XMP_StringPtr propName,
1176
XMP_OptionBits options )
1178
XMP_Assert ( (schemaNS != 0) && (propName != 0) ); // Enforced by wrapper.
1180
XMP_StringPtr valueStr;
1181
XMP_StringLen valueLen;
1183
XMPUtils::ConvertFromFloat ( propValue, "", &valueStr, &valueLen );
1184
SetProperty ( schemaNS, propName, valueStr, options );
1186
} // SetProperty_Float
1189
// -------------------------------------------------------------------------------------------------
1194
XMPMeta::SetProperty_Date ( XMP_StringPtr schemaNS,
1195
XMP_StringPtr propName,
1196
const XMP_DateTime & propValue,
1197
XMP_OptionBits options )
1199
XMP_Assert ( (schemaNS != 0) && (propName != 0) ); // Enforced by wrapper.
1201
XMP_StringPtr valueStr;
1202
XMP_StringLen valueLen;
1204
XMPUtils::ConvertFromDate ( propValue, &valueStr, &valueLen );
1205
SetProperty ( schemaNS, propName, valueStr, options );
1207
} // SetProperty_Date
1209
// =================================================================================================