~ubuntu-branches/debian/lenny/exiv2/lenny

« back to all changes in this revision

Viewing changes to xmpsdk/src/XMPMeta.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Mark Purcell
  • Date: 2008-06-21 08:23:53 UTC
  • mfrom: (1.1.5 upstream)
  • Revision ID: james.westby@ubuntu.com-20080621082353-b1n4w08trwfwbfl4
Tags: 0.17.1-1
* New upstream release
  - Library transition cleared on debian-release/ d-d-a
* Version 0.17 also fixes:
  - CVE-2008-2696: DoS via metadata in images (Closes: #486328)
  - crashes when fed with wrong file (Closes: #485670)
* Urgency medium for CVE fix
* debian/patches/gcc4.3.diff unecessary for gcc-4.3
* Add /usr/share/bug/exiv2/presubj message for reportbug(1)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
// 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
 
 
23
#if XMP_DebugBuild
 
24
        #include <iostream>
 
25
#endif
 
26
 
 
27
using namespace std;
 
28
 
 
29
#if XMP_WinBuild
 
30
        #pragma warning ( disable : 4533 )      // initialization of '...' is skipped by 'goto ...'
 
31
        #pragma warning ( disable : 4702 )      // unreachable code
 
32
        #pragma warning ( disable : 4800 )      // forcing value to bool 'true' or 'false' (performance warning)
 
33
        #pragma warning ( disable : 4996 )      // '...' was declared deprecated
 
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
 
 
47
// =================================================================================================
 
48
// Static Variables
 
49
// ================
 
50
 
 
51
XMP_VarString * xdefaultName = 0;
 
52
 
 
53
// These are embedded version strings.
 
54
 
 
55
const char * kXMPCore_EmbeddedVersion   = kXMPCore_VersionMessage;
 
56
const char * kXMPCore_EmbeddedCopyright = kXMPCoreName " " kXMP_CopyrightStr;
 
57
 
 
58
// =================================================================================================
 
59
// Local Utilities
 
60
// ===============
 
61
 
 
62
#define IsHexDigit(ch)          ( (('0' <= (ch)) && ((ch) <= '9')) || (('A' <= (ch)) && ((ch) <= 'F')) )
 
63
#define HexDigitValue(ch)       ( (((ch) - '0') < 10) ? ((ch) - '0') : ((ch) - 'A' + 10) )
 
64
 
 
65
static const char * kTenSpaces = "          ";
 
66
#define OutProcPadding(pad)     { size_t padLen = (pad);                                                                                                \
 
67
                                                          for ( ; padLen >= 10; padLen -= 10 ) OutProcNChars ( kTenSpaces, 10 );        \
 
68
                                                          for ( ; padLen > 0; padLen -= 1 ) OutProcNChars ( " ", 1 ); }
 
69
 
 
70
 
 
71
#define OutProcNewline()        { status = (*outProc) ( refCon, "\n", 1 );  if ( status != 0 ) goto EXIT; }
 
72
 
 
73
#define OutProcNChars(p,n)      { status = (*outProc) ( refCon, (p), (n) );  if ( status != 0 ) goto EXIT; }
 
74
 
 
75
#define OutProcLiteral(lit)     { status = (*outProc) ( refCon, (lit), strlen(lit) );  if ( status != 0 ) goto EXIT; }
 
76
 
 
77
#define OutProcString(str)      { status = (*outProc) ( refCon, (str).c_str(), (str).size() );  if ( status != 0 ) goto EXIT; }
 
78
 
 
79
#define OutProcULong(num)       { snprintf ( buffer, sizeof(buffer), "%lu", (num) ); /* AUDIT: Using sizeof for snprintf length is safe */ \
 
80
                                                          status = (*outProc) ( refCon, buffer, strlen(buffer) );  if ( status != 0 ) goto EXIT; }
 
81
 
 
82
#define OutProcHexInt(num)      { snprintf ( buffer, sizeof(buffer), "%lX", (num) ); /* AUDIT: Using sizeof for snprintf length is safe */      \
 
83
                                                          status = (*outProc) ( refCon, buffer, strlen(buffer) );  if ( status != 0 ) goto EXIT; }
 
84
 
 
85
static const char * kIndent = "   ";
 
86
#define OutProcIndent(lev)      { for ( size_t i = 0; i < (lev); ++i ) OutProcNChars ( kIndent, 3 ); }
 
87
 
 
88
 
 
89
// -------------------------------------------------------------------------------------------------
 
90
// DumpStringMap
 
91
// -------------
 
92
 
 
93
static XMP_Status
 
94
DumpStringMap ( const XMP_StringMap & map, XMP_StringPtr label, XMP_TextOutputProc outProc, void * refCon )
 
95
{
 
96
        XMP_Status                      status;
 
97
        XMP_cStringMapPos       currPos;
 
98
        XMP_cStringMapPos       endPos = map.end();
 
99
        
 
100
        size_t maxLen = 0;
 
101
        for ( currPos = map.begin(); currPos != endPos; ++currPos ) {
 
102
                size_t currLen = currPos->first.size();
 
103
                if ( currLen > maxLen ) maxLen = currLen;
 
104
        }
 
105
        
 
106
        OutProcNewline();
 
107
        OutProcLiteral ( label );
 
108
        OutProcNewline();
 
109
        
 
110
        for ( currPos = map.begin(); currPos != endPos; ++currPos ) {
 
111
                OutProcNChars ( "  ", 2 );
 
112
                OutProcString ( currPos->first );
 
113
                OutProcPadding ( maxLen - currPos->first.size() );
 
114
                OutProcNChars ( " => ", 4 );
 
115
                OutProcString ( currPos->second );
 
116
                OutProcNewline();
 
117
        }
 
118
        
 
119
EXIT:
 
120
        return status;
 
121
        
 
122
}       // DumpStringMap
 
123
 
 
124
 
 
125
// -------------------------------------------------------------------------------------------------
 
126
// DumpNodeOptions
 
127
// ---------------
 
128
 
 
129
static XMP_Status
 
130
DumpNodeOptions ( XMP_OptionBits         options,
 
131
                                  XMP_TextOutputProc outProc,
 
132
                                  void *                         refCon )
 
133
{
 
134
        XMP_Status      status;
 
135
        char            buffer [32];    // Decimal of a 64 bit int is at most about 20 digits.
 
136
 
 
137
        static const char * optNames[] = { " schema",           // 0x8000_0000
 
138
                                                                           " ?30",
 
139
                                                                           " ?29",
 
140
                                                                           " -COMMAS-",
 
141
                                                                           " ?27",                      // 0x0800_0000
 
142
                                                                           " ?26",
 
143
                                                                           " ?25",
 
144
                                                                           " ?24",
 
145
                                                                           " ?23",                      // 0x0080_0000
 
146
                                                                           " isStale",
 
147
                                                                           " isDerived",
 
148
                                                                           " isStable",
 
149
                                                                           " ?19",                      // 0x0008_0000
 
150
                                                                           " isInternal",
 
151
                                                                           " hasAliases",
 
152
                                                                           " isAlias",
 
153
                                                                           " -AFTER-",          // 0x0000_8000
 
154
                                                                           " -BEFORE-",
 
155
                                                                           " isCompact",
 
156
                                                                           " isLangAlt",
 
157
                                                                           " isAlt",            // 0x0000_0800
 
158
                                                                           " isOrdered",
 
159
                                                                           " isArray",
 
160
                                                                           " isStruct",
 
161
                                                                           " hasType",          // 0x0000_0080
 
162
                                                                           " hasLang",
 
163
                                                                           " isQual",
 
164
                                                                           " hasQual",
 
165
                                                                           " ?3",                       // 0x0000_0008
 
166
                                                                           " ?2",
 
167
                                                                           " URI",
 
168
                                                                           " ?0" };
 
169
 
 
170
        if ( options == 0 ) {
 
171
        
 
172
                OutProcNChars ( "(0x0)", 5 );
 
173
        
 
174
        } else {
 
175
        
 
176
                OutProcNChars ( "(0x", 3 );
 
177
                OutProcHexInt ( options );
 
178
                OutProcNChars ( " :", 2 );
 
179
 
 
180
                XMP_OptionBits mask = 0x80000000;
 
181
                for ( int b = 0; b < 32; ++b ) {
 
182
                        if ( options & mask ) OutProcLiteral ( optNames[b] );
 
183
                        mask = mask >> 1;
 
184
                }
 
185
                OutProcNChars ( ")", 1 );
 
186
        
 
187
        }
 
188
        
 
189
EXIT:
 
190
        return status;
 
191
 
 
192
}       // DumpNodeOptions
 
193
 
 
194
 
 
195
// -------------------------------------------------------------------------------------------------
 
196
// DumpPropertyTree
 
197
// ----------------
 
198
 
 
199
// *** Extract the validation code into a separate routine to call on exit in debug builds.
 
200
 
 
201
static XMP_Status
 
202
DumpPropertyTree ( const XMP_Node *       currNode,
 
203
                                   int                            indent,
 
204
                                   size_t                         itemIndex,
 
205
                                   XMP_TextOutputProc outProc,
 
206
                                   void *                         refCon )
 
207
{
 
208
        XMP_Status      status;
 
209
        char            buffer [32];    // Decimal of a 64 bit int is at most about 20 digits.
 
210
 
 
211
        OutProcIndent ( (size_t)indent );
 
212
        if ( itemIndex == 0 ) {
 
213
                if ( currNode->options & kXMP_PropIsQualifier ) OutProcNChars ( "? ", 2 );
 
214
                OutProcString ( currNode->name );
 
215
        } else {
 
216
                OutProcNChars ( "[", 1 );
 
217
                OutProcULong ( static_cast<unsigned long>(itemIndex) );
 
218
                OutProcNChars ( "]", 1 );
 
219
        }
 
220
 
 
221
        if ( ! (currNode->options & kXMP_PropCompositeMask) ) {
 
222
                OutProcNChars ( " = \"", 4 );
 
223
                OutProcString ( currNode->value );
 
224
                OutProcNChars ( "\"", 1 );
 
225
        }
 
226
 
 
227
        if ( currNode->options != 0 ) {
 
228
                OutProcNChars ( "  ", 2 );
 
229
                status = DumpNodeOptions ( currNode->options, outProc, refCon );
 
230
                if ( status != 0 ) goto EXIT;
 
231
        }
 
232
        
 
233
        if ( currNode->options & kXMP_PropHasLang ) {
 
234
                if ( currNode->qualifiers.empty() || (currNode->qualifiers[0]->name != "xml:lang") ) {
 
235
                        OutProcLiteral ( "  ** bad lang flag **" );
 
236
                }
 
237
        }
 
238
        // *** Check rdf:type also.
 
239
 
 
240
        if ( ! (currNode->options & kXMP_PropCompositeMask) ) {
 
241
                if ( ! currNode->children.empty() ) OutProcLiteral ( "  ** bad children **" );
 
242
        } else if ( currNode->options & kXMP_PropValueIsArray ) {
 
243
                if ( currNode->options & kXMP_PropValueIsStruct ) OutProcLiteral ( "  ** bad comp flags **" );
 
244
        } else if ( (currNode->options & kXMP_PropCompositeMask) != kXMP_PropValueIsStruct ) {
 
245
                OutProcLiteral ( "  ** bad comp flags **" );
 
246
        }
 
247
 
 
248
        #if 0   // *** XMP_DebugBuild
 
249
                if ( (currNode->_namePtr != currNode->name.c_str()) ||
 
250
                         (currNode->_valuePtr != currNode->value.c_str()) ) OutProcLiteral ( "  ** bad debug string **" );
 
251
        #endif
 
252
        
 
253
        OutProcNewline();
 
254
        
 
255
        for ( size_t qualNum = 0, qualLim = currNode->qualifiers.size(); qualNum < qualLim; ++qualNum ) {
 
256
 
 
257
                const XMP_Node * currQual = currNode->qualifiers[qualNum];
 
258
 
 
259
                if ( currQual->parent != currNode ) OutProcLiteral ( "** bad parent link => " );
 
260
                if ( currQual->name == kXMP_ArrayItemName ) OutProcLiteral ( "** bad qual name => " );
 
261
                if ( ! (currQual->options & kXMP_PropIsQualifier) ) OutProcLiteral ( "** bad qual flag => " );
 
262
                if ( currQual->name == "xml:lang" ) {
 
263
                        if ( (qualNum != 0) || (! (currNode->options & kXMP_PropHasLang)) ) OutProcLiteral ( "** bad lang qual => " );
 
264
                }
 
265
 
 
266
                status = DumpPropertyTree ( currQual, indent+2, 0, outProc, refCon );
 
267
                if ( status != 0 ) goto EXIT;
 
268
 
 
269
        }
 
270
 
 
271
        for ( size_t childNum = 0, childLim = currNode->children.size(); childNum < childLim; ++childNum ) {
 
272
 
 
273
                const XMP_Node * currChild = currNode->children[childNum];
 
274
 
 
275
                if ( currChild->parent != currNode ) OutProcLiteral ( "** bad parent link => " );
 
276
                if ( currChild->options & kXMP_PropIsQualifier ) OutProcLiteral ( "** bad qual flag => " );
 
277
        
 
278
                if ( currNode->options & kXMP_PropValueIsArray ) {
 
279
                        itemIndex = childNum+1;
 
280
                        if ( currChild->name != kXMP_ArrayItemName ) OutProcLiteral ( "** bad item name => " );
 
281
                } else {
 
282
                        itemIndex = 0;
 
283
                        if ( currChild->name == kXMP_ArrayItemName ) OutProcLiteral ( "** bad field name => " );
 
284
                }
 
285
 
 
286
                status = DumpPropertyTree ( currChild, indent+1, itemIndex, outProc, refCon );
 
287
                if ( status != 0 ) goto EXIT;
 
288
 
 
289
        }
 
290
        
 
291
EXIT:
 
292
        return status;
 
293
 
 
294
}       // DumpPropertyTree
 
295
 
 
296
 
 
297
// -------------------------------------------------------------------------------------------------
 
298
// DumpXMLTree
 
299
// -----------
 
300
 
 
301
#if DumpXMLParseTree
 
302
 
 
303
static inline void PutHexByte ( FILE * log, unsigned char ch )
 
304
{
 
305
 
 
306
        fprintf ( log, "\\x" );
 
307
        if ( ch < 0x10 ) {
 
308
                fprintf ( log, "%c", kHexDigits[ch] );
 
309
        } else {
 
310
                fprintf ( log, "%c%c", kHexDigits[ch>>4], kHexDigits[ch&0xF] );
 
311
        }
 
312
        
 
313
}       // PutHexByte
 
314
 
 
315
// -------------------------------------------------------------------------------------------------
 
316
 
 
317
static void PutClearString ( FILE * log, const std::string & str )
 
318
{
 
319
 
 
320
        for ( size_t i = 0; i != str.size(); ++i ) {
 
321
                unsigned char ch = str[i];
 
322
                if ( (0x20 <= ch) && (ch <= 0x7F) ) {
 
323
                        fprintf ( log, "%c", ch );
 
324
                } else {
 
325
                        PutHexByte ( log, ch );
 
326
                }
 
327
        }
 
328
 
 
329
}       // PutClearString
 
330
 
 
331
// -------------------------------------------------------------------------------------------------
 
332
 
 
333
static void DumpXMLTree ( FILE * log, const XML_Node & node, int indent )
 
334
{
 
335
        size_t i;
 
336
        
 
337
        #if 0   // *** XMP_DebugBuild
 
338
                if ( (node._namePtr != node.name.c_str()) ||
 
339
                         (node._valuePtr != node.value.c_str()) ) fprintf ( log, "*** bad debug string ***\n" );
 
340
        #endif
 
341
        
 
342
        for ( i = 0; i != (size_t)indent; ++i ) fprintf ( log, "  " );
 
343
        
 
344
        switch ( node.kind ) {
 
345
        
 
346
                case kRootNode :
 
347
                        fprintf ( log, "\nStart of XML tree dump\n\n" );
 
348
                        if ( (indent != 0) || (! node.attrs.empty()) ||
 
349
                                 (! node.ns.empty()) || (! node.name.empty()) || (!node.value.empty()) ) fprintf ( log, " ** invalid root ** \n" );
 
350
                        for ( i = 0; i < node.children.size(); ++i ) {
 
351
                                XMP_Uns8 kind = node.children[i]->kind;
 
352
                                if ( (kind == kRootNode) || (kind == kAttrNode) ) fprintf ( log, " ** invalid child ** \n" );
 
353
                                DumpXMLTree ( log, *node.children[i], indent+1 );
 
354
                        }
 
355
                        fprintf ( log, "\nEnd of XML tree dump\n" );
 
356
                        break;
 
357
        
 
358
                case kElemNode :
 
359
                        fprintf ( log, "Elem %s", node.name.c_str() );
 
360
                        if ( indent == 0 ) fprintf ( log, " ** invalid elem ** " );
 
361
                        if ( ! node.ns.empty() ) fprintf ( log, " @ %s", node.ns.c_str() );
 
362
                        fprintf ( log, "\n" );
 
363
                        for ( i = 0; i < node.attrs.size(); ++i ) {
 
364
                                XMP_Uns8 kind = node.attrs[i]->kind;
 
365
                                if ( kind != kAttrNode ) fprintf ( log, " ** invalid attr ** \n" );
 
366
                                DumpXMLTree ( log, *node.attrs[i], indent+2 );
 
367
                        }
 
368
                        for ( i = 0; i < node.children.size(); ++i ) {
 
369
                                XMP_Uns8 kind = node.children[i]->kind;
 
370
                                if ( (kind == kRootNode) || (kind == kAttrNode) ) fprintf ( log, " ** invalid child ** \n" );
 
371
                                DumpXMLTree ( log, *node.children[i], indent+1 );
 
372
                        }
 
373
                        break;
 
374
        
 
375
                case kAttrNode :
 
376
                        fprintf ( log, "Attr %s", node.name.c_str() );
 
377
                        if ( (indent == 0) || node.name.empty() || (! node.attrs.empty()) || (! node.children.empty()) ) fprintf ( log, " ** invalid attr ** " );
 
378
                        fprintf ( log, " = \"" );
 
379
                        PutClearString ( log, node.value );
 
380
                        fprintf ( log, "\"" );
 
381
                        if ( ! node.ns.empty() ) fprintf ( log, " @ %s", node.ns.c_str() );
 
382
                        fprintf ( log, "\n" );
 
383
                        break;
 
384
        
 
385
                case kCDataNode :
 
386
                        if ( (indent == 0) || (! node.ns.empty()) || (! node.name.empty()) ||
 
387
                                 (! node.attrs.empty()) || (! node.children.empty()) ) fprintf ( log, " ** invalid cdata ** \n" );
 
388
                        fprintf ( log, "\"" );
 
389
                        PutClearString ( log, node.value );
 
390
                        fprintf ( log, "\"\n" );
 
391
                        break;
 
392
        
 
393
                case kPINode :
 
394
                        fprintf ( log, "PI %s", node.name.c_str() );
 
395
                        if ( (indent == 0) || node.name.empty() || (! node.children.empty()) ) fprintf ( log, " ** invalid pi ** \n" );
 
396
                        if ( ! node.value.empty() ) {
 
397
                                fprintf ( log, " <? " );
 
398
                                PutClearString ( log, node.value );
 
399
                                fprintf ( log, " ?>" );
 
400
                        }
 
401
                        fprintf ( log, "\n" );
 
402
                        break;
 
403
        
 
404
        }
 
405
 
 
406
}       // DumpXMLTree
 
407
 
 
408
#endif  // DumpXMLParseTree
 
409
 
 
410
 
 
411
// =================================================================================================
 
412
// Constructors
 
413
// ============
 
414
 
 
415
 
 
416
XMPMeta::XMPMeta() : clientRefs(0), prevTkVer(0), tree(XMP_Node(0,"",0)), xmlParser(0)
 
417
{
 
418
        // Nothing more to do, clientRefs is incremented in wrapper.
 
419
        #if XMP_TraceCTorDTor
 
420
                printf ( "Default construct XMPMeta @ %.8X\n", this );
 
421
        #endif
 
422
}       // XMPMeta
 
423
 
 
424
// -------------------------------------------------------------------------------------------------
 
425
 
 
426
XMPMeta::~XMPMeta() RELEASE_NO_THROW
 
427
{
 
428
        #if XMP_TraceCTorDTor
 
429
                printf ( "Destruct XMPMeta @ %.8X\n", this );
 
430
        #endif
 
431
 
 
432
        XMP_Assert ( this->clientRefs <= 0 );
 
433
        if ( xmlParser != 0 ) delete ( xmlParser );
 
434
        xmlParser = 0;
 
435
 
 
436
}       // ~XMPMeta
 
437
 
 
438
 
 
439
// =================================================================================================
 
440
// Class Static Functions
 
441
// ======================
 
442
//
 
443
//
 
444
// =================================================================================================
 
445
 
 
446
// -------------------------------------------------------------------------------------------------
 
447
// GetVersionInfo
 
448
// --------------
 
449
 
 
450
/* class-static */ void
 
451
XMPMeta::GetVersionInfo ( XMP_VersionInfo * info )
 
452
{
 
453
 
 
454
        memset ( info, 0, sizeof(XMP_VersionInfo) );
 
455
        
 
456
        info->major   = XMP_API_VERSION_MAJOR;
 
457
        info->minor   = XMP_API_VERSION_MINOR;
 
458
        info->micro   = XMP_API_VERSION_MICRO;
 
459
        info->isDebug = kXMPCore_DebugFlag;
 
460
        info->flags   = 0;      // ! None defined yet.
 
461
        info->message = kXMPCore_VersionMessage;
 
462
        
 
463
}       // GetVersionInfo
 
464
 
 
465
// -------------------------------------------------------------------------------------------------
 
466
// Initialize
 
467
// ----------
 
468
 
 
469
/* class-static */ bool
 
470
XMPMeta::Initialize()
 
471
{
 
472
        // Allocate and initialize static objects.
 
473
        
 
474
        ++sXMP_InitCount;
 
475
        if ( sXMP_InitCount > 1 ) return true;
 
476
        
 
477
        #if TraceXMPCalls
 
478
                // xmpOut = fopen ( "xmp.out", "w" );   // Coordinate with client glue in WXMP_Common.hpp
 
479
                fprintf ( xmpOut, "XMP initializing\n" ); fflush ( xmpOut );
 
480
        #endif
 
481
        
 
482
        sExceptionMessage = new XMP_VarString();
 
483
        XMP_InitMutex ( &sXMPCoreLock );
 
484
    sOutputNS  = new XMP_VarString;
 
485
    sOutputStr = new XMP_VarString;
 
486
 
 
487
        xdefaultName = new XMP_VarString ( "x-default" );
 
488
        
 
489
        sNamespaceURIToPrefixMap        = new XMP_StringMap;
 
490
        sNamespacePrefixToURIMap        = new XMP_StringMap;
 
491
        sRegisteredAliasMap                     = new XMP_AliasMap;
 
492
        
 
493
        InitializeUnicodeConversions();
 
494
        
 
495
        // Register standard namespaces and aliases.
 
496
        
 
497
        XMP_StringPtr   voidPtr;
 
498
        XMP_StringLen   voidLen;
 
499
        
 
500
        (void) RegisterNamespace ( kXMP_NS_XML, "xml", &voidPtr, &voidLen );
 
501
        (void) RegisterNamespace ( kXMP_NS_RDF, "rdf", &voidPtr, &voidLen );
 
502
        (void) RegisterNamespace ( kXMP_NS_DC, "dc", &voidPtr, &voidLen );
 
503
 
 
504
        (void) RegisterNamespace ( kXMP_NS_XMP, "xap", &voidPtr, &voidLen );
 
505
        (void) RegisterNamespace ( kXMP_NS_PDF, "pdf", &voidPtr, &voidLen );
 
506
        (void) RegisterNamespace ( kXMP_NS_Photoshop, "photoshop", &voidPtr, &voidLen );
 
507
        (void) RegisterNamespace ( kXMP_NS_PSAlbum, "album", &voidPtr, &voidLen );
 
508
        (void) RegisterNamespace ( kXMP_NS_EXIF, "exif", &voidPtr, &voidLen );
 
509
        (void) RegisterNamespace ( kXMP_NS_EXIF_Aux, "aux", &voidPtr, &voidLen );
 
510
        (void) RegisterNamespace ( kXMP_NS_TIFF, "tiff", &voidPtr, &voidLen );
 
511
        (void) RegisterNamespace ( kXMP_NS_PNG, "png", &voidPtr, &voidLen );
 
512
        (void) RegisterNamespace ( kXMP_NS_JPEG, "jpeg", &voidPtr, &voidLen );
 
513
        (void) RegisterNamespace ( kXMP_NS_JP2K, "jp2k", &voidPtr, &voidLen );
 
514
        (void) RegisterNamespace ( kXMP_NS_CameraRaw, "crs", &voidPtr, &voidLen );
 
515
        (void) RegisterNamespace ( kXMP_NS_ASF, "asf", &voidPtr, &voidLen );
 
516
        (void) RegisterNamespace ( kXMP_NS_WAV, "wav", &voidPtr, &voidLen );
 
517
 
 
518
        (void) RegisterNamespace ( kXMP_NS_AdobeStockPhoto, "bmsp", &voidPtr, &voidLen );
 
519
 
 
520
        (void) RegisterNamespace ( kXMP_NS_XMP_Rights, "xapRights", &voidPtr, &voidLen );
 
521
        (void) RegisterNamespace ( kXMP_NS_XMP_MM, "xapMM", &voidPtr, &voidLen );
 
522
        (void) RegisterNamespace ( kXMP_NS_XMP_BJ, "xapBJ", &voidPtr, &voidLen );
 
523
        (void) RegisterNamespace ( kXMP_NS_XMP_Note, "xmpNote", &voidPtr, &voidLen );
 
524
 
 
525
        (void) RegisterNamespace ( kXMP_NS_DM, "xmpDM", &voidPtr, &voidLen );
 
526
        (void) RegisterNamespace ( kXMP_NS_XMP_Text, "xapT", &voidPtr, &voidLen );
 
527
        (void) RegisterNamespace ( kXMP_NS_XMP_PagedFile, "xapTPg", &voidPtr, &voidLen );
 
528
        (void) RegisterNamespace ( kXMP_NS_XMP_Graphics, "xapG", &voidPtr, &voidLen );
 
529
        (void) RegisterNamespace ( kXMP_NS_XMP_Image, "xapGImg", &voidPtr, &voidLen );
 
530
 
 
531
        (void) RegisterNamespace ( kXMP_NS_XMP_Font, "stFnt", &voidPtr, &voidLen );
 
532
        (void) RegisterNamespace ( kXMP_NS_XMP_Dimensions, "stDim", &voidPtr, &voidLen );
 
533
        (void) RegisterNamespace ( kXMP_NS_XMP_ResourceEvent, "stEvt", &voidPtr, &voidLen );
 
534
        (void) RegisterNamespace ( kXMP_NS_XMP_ResourceRef, "stRef", &voidPtr, &voidLen );
 
535
        (void) RegisterNamespace ( kXMP_NS_XMP_ST_Version, "stVer", &voidPtr, &voidLen );
 
536
        (void) RegisterNamespace ( kXMP_NS_XMP_ST_Job, "stJob", &voidPtr, &voidLen );
 
537
        (void) RegisterNamespace ( kXMP_NS_XMP_ManifestItem, "stMfs", &voidPtr, &voidLen );
 
538
 
 
539
        (void) RegisterNamespace ( kXMP_NS_XMP_IdentifierQual, "xmpidq", &voidPtr, &voidLen );
 
540
 
 
541
        (void) RegisterNamespace ( kXMP_NS_IPTCCore, "Iptc4xmpCore", &voidPtr, &voidLen );
 
542
 
 
543
        (void) RegisterNamespace ( kXMP_NS_PDFA_Schema, "pdfaSchema", &voidPtr, &voidLen );
 
544
        (void) RegisterNamespace ( kXMP_NS_PDFA_Property, "pdfaProperty", &voidPtr, &voidLen );
 
545
        (void) RegisterNamespace ( kXMP_NS_PDFA_Type, "pdfaType", &voidPtr, &voidLen );
 
546
        (void) RegisterNamespace ( kXMP_NS_PDFA_Field, "pdfaField", &voidPtr, &voidLen );
 
547
        (void) RegisterNamespace ( kXMP_NS_PDFA_ID, "pdfaid", &voidPtr, &voidLen );
 
548
        (void) RegisterNamespace ( kXMP_NS_PDFA_Extension, "pdfaExtension", &voidPtr, &voidLen );
 
549
 
 
550
        (void) RegisterNamespace ( kXMP_NS_PDFX, "pdfx", &voidPtr, &voidLen );
 
551
        (void) RegisterNamespace ( kXMP_NS_PDFX_ID, "pdfxid", &voidPtr, &voidLen );
 
552
        
 
553
        (void) RegisterNamespace ( "adobe:ns:meta/", "x", &voidPtr, &voidLen );
 
554
        (void) RegisterNamespace ( "http://ns.adobe.com/iX/1.0/", "iX", &voidPtr, &voidLen );
 
555
 
 
556
// 06-Oct-07, ahu: Do not use aliases. They result in unexpected behaviour.
 
557
//      XMPMeta::RegisterStandardAliases ( "" );
 
558
        
 
559
        // Initialize the other core classes.
 
560
        
 
561
        if ( ! XMPIterator::Initialize() ) XMP_Throw ( "Failure from XMPIterator::Initialize", kXMPErr_InternalFailure );
 
562
        if ( ! XMPUtils::Initialize() ) XMP_Throw ( "Failure from XMPUtils::Initialize", kXMPErr_InternalFailure );
 
563
 
 
564
        // Do miscelaneous semantic checks of types and arithmetic.
 
565
 
 
566
        XMP_Assert ( sizeof(XMP_Int8) == 1 );
 
567
        XMP_Assert ( sizeof(XMP_Int16) == 2 );
 
568
        XMP_Assert ( sizeof(XMP_Int32) == 4 );
 
569
        XMP_Assert ( sizeof(XMP_Int64) == 8 );
 
570
        XMP_Assert ( sizeof(XMP_Uns8) == 1 );
 
571
        XMP_Assert ( sizeof(XMP_Uns16) == 2 );
 
572
        XMP_Assert ( sizeof(XMP_Uns32) == 4 );
 
573
        XMP_Assert ( sizeof(XMP_Uns64) == 8 );
 
574
        
 
575
        XMP_Assert ( sizeof(XMP_OptionBits) == 4 );     // Check that option masking work on all 32 bits.
 
576
        XMP_OptionBits flag = ~0UL;
 
577
        XMP_Assert ( flag == (XMP_OptionBits)(-1L) );
 
578
        XMP_Assert ( (flag ^ kXMP_PropHasLang) == 0xFFFFFFBFUL );
 
579
        XMP_Assert ( (flag & ~kXMP_PropHasLang) == 0xFFFFFFBFUL );
 
580
        
 
581
        XMP_OptionBits opt1 = 0;        // Check the general option bit macros.
 
582
        XMP_OptionBits opt2 = ~0UL;
 
583
        XMP_SetOption ( opt1, kXMP_PropValueIsArray );
 
584
        XMP_ClearOption ( opt2, kXMP_PropValueIsArray );
 
585
        XMP_Assert ( opt1 == ~opt2 );
 
586
        XMP_Assert ( XMP_TestOption ( opt1, kXMP_PropValueIsArray ) );
 
587
        XMP_Assert ( ! XMP_TestOption ( opt2, kXMP_PropValueIsArray ) );
 
588
        
 
589
        XMP_Assert ( XMP_PropIsSimple ( ~kXMP_PropCompositeMask ) );    // Check the special option bit macros.
 
590
        XMP_Assert ( ! XMP_PropIsSimple ( kXMP_PropValueIsStruct ) );
 
591
        XMP_Assert ( ! XMP_PropIsSimple ( kXMP_PropValueIsArray ) );
 
592
 
 
593
        XMP_Assert ( XMP_PropIsStruct ( kXMP_PropValueIsStruct ) );
 
594
        XMP_Assert ( XMP_PropIsArray  ( kXMP_PropValueIsArray ) );
 
595
        XMP_Assert ( ! XMP_PropIsStruct ( ~kXMP_PropValueIsStruct ) );
 
596
        XMP_Assert ( ! XMP_PropIsArray  ( ~kXMP_PropValueIsArray ) );
 
597
        
 
598
        XMP_Assert ( XMP_ArrayIsUnordered ( ~kXMP_PropArrayIsOrdered ) );
 
599
        XMP_Assert ( XMP_ArrayIsOrdered   ( kXMP_PropArrayIsOrdered ) );
 
600
        XMP_Assert ( XMP_ArrayIsAlternate ( kXMP_PropArrayIsAlternate ) );
 
601
        XMP_Assert ( XMP_ArrayIsAltText   ( kXMP_PropArrayIsAltText ) );
 
602
        XMP_Assert ( ! XMP_ArrayIsUnordered ( kXMP_PropArrayIsOrdered ) );
 
603
        XMP_Assert ( ! XMP_ArrayIsOrdered   ( ~kXMP_PropArrayIsOrdered ) );
 
604
        XMP_Assert ( ! XMP_ArrayIsAlternate ( ~kXMP_PropArrayIsAlternate ) );
 
605
        XMP_Assert ( ! XMP_ArrayIsAltText   ( ~kXMP_PropArrayIsAltText ) );
 
606
        
 
607
        XMP_Assert ( XMP_PropHasQualifiers ( kXMP_PropHasQualifiers ) );
 
608
        XMP_Assert ( XMP_PropIsQualifier   ( kXMP_PropIsQualifier ) );
 
609
        XMP_Assert ( XMP_PropHasLang       ( kXMP_PropHasLang ) );
 
610
        XMP_Assert ( ! XMP_PropHasQualifiers ( ~kXMP_PropHasQualifiers ) );
 
611
        XMP_Assert ( ! XMP_PropIsQualifier   ( ~kXMP_PropIsQualifier ) );
 
612
        XMP_Assert ( ! XMP_PropHasLang       ( ~kXMP_PropHasLang ) );
 
613
        
 
614
        XMP_Assert ( XMP_NodeIsSchema ( kXMP_SchemaNode ) );
 
615
        XMP_Assert ( XMP_PropIsAlias  ( kXMP_PropIsAlias ) );
 
616
        XMP_Assert ( ! XMP_NodeIsSchema ( ~kXMP_SchemaNode ) );
 
617
        XMP_Assert ( ! XMP_PropIsAlias  ( ~kXMP_PropIsAlias ) );
 
618
        
 
619
        #if 0   // Generally off, enable to hand check generated code.
 
620
                extern XMP_OptionBits opt3, opt4;
 
621
                if ( XMP_TestOption ( opt3, kXMP_PropValueIsArray ) ) opt4 = opt3;
 
622
                if ( ! XMP_TestOption ( opt3, kXMP_PropValueIsStruct ) ) opt4 = opt3;
 
623
                static bool ok1 = XMP_TestOption ( opt4, kXMP_PropValueIsArray );
 
624
                static bool ok2 = ! XMP_TestOption ( opt4, kXMP_PropValueIsStruct );
 
625
        #endif
 
626
        
 
627
        // Make sure the embedded info strings are referenced and kept.
 
628
        if ( (kXMPCore_EmbeddedVersion[0] == 0) || (kXMPCore_EmbeddedCopyright[0] == 0) ) return false;
 
629
        return true;
 
630
 
 
631
}       // Initialize
 
632
 
 
633
 
 
634
// -------------------------------------------------------------------------------------------------
 
635
// Terminate
 
636
// ---------
 
637
 
 
638
#define EliminateGlobal(g) delete ( g ); g = 0
 
639
 
 
640
/* class-static */ void
 
641
XMPMeta::Terminate() RELEASE_NO_THROW
 
642
{
 
643
        --sXMP_InitCount;
 
644
        if ( sXMP_InitCount > 0 ) return;
 
645
 
 
646
        #if TraceXMPCalls
 
647
                fprintf ( xmpOut, "XMP terminating\n" ); fflush ( xmpOut );
 
648
                // fclose ( xmpOut );   // Coordinate with fopen in XMPMeta::Initialize.
 
649
        #endif
 
650
        
 
651
        XMPIterator::Terminate();
 
652
        XMPUtils::Terminate();
 
653
 
 
654
        EliminateGlobal ( sNamespaceURIToPrefixMap );
 
655
        EliminateGlobal ( sNamespacePrefixToURIMap );
 
656
        EliminateGlobal ( sRegisteredAliasMap );
 
657
    
 
658
    EliminateGlobal ( xdefaultName );
 
659
    EliminateGlobal ( sOutputNS );
 
660
    EliminateGlobal ( sOutputStr );
 
661
        EliminateGlobal ( sExceptionMessage );
 
662
 
 
663
        XMP_TermMutex ( sXMPCoreLock );
 
664
 
 
665
}       // Terminate
 
666
 
 
667
 
 
668
// -------------------------------------------------------------------------------------------------
 
669
// Unlock
 
670
// ------
 
671
 
 
672
/* class-static */ void
 
673
XMPMeta::Unlock ( XMP_OptionBits options )
 
674
{
 
675
        options = options;      // Avoid unused parameter warning.      // *** Need IgnoreParam macro.
 
676
 
 
677
    #if TraceXMPLocking
 
678
        fprintf ( xmpOut, "  Unlocking XMP toolkit, count = %d\n", sLockCount ); fflush ( xmpOut );
 
679
        #endif
 
680
    --sLockCount;
 
681
    XMP_Assert ( sLockCount == 0 );
 
682
        XMP_ExitCriticalRegion ( sXMPCoreLock );
 
683
 
 
684
}       // Unlock
 
685
 
 
686
 
 
687
// -------------------------------------------------------------------------------------------------
 
688
// UnlockObject
 
689
// ------------
 
690
 
 
691
void
 
692
XMPMeta::UnlockObject ( XMP_OptionBits options ) const
 
693
{
 
694
        options = options;      // Avoid unused parameter warning.
 
695
 
 
696
        XMPMeta::Unlock ( 0 );
 
697
 
 
698
}       // UnlockObject
 
699
 
 
700
 
 
701
// -------------------------------------------------------------------------------------------------
 
702
// DumpNamespaces
 
703
// --------------
 
704
//
 
705
// Dump the prefix to URI map (easier to read) and verify that both are consistent and legit.
 
706
 
 
707
// *** Should put checks in a separate routine for regular calling in debug builds.
 
708
 
 
709
/* class-static */ XMP_Status
 
710
XMPMeta::DumpNamespaces ( XMP_TextOutputProc outProc,
 
711
                          void *             refCon )
 
712
{
 
713
        XMP_Assert ( outProc != 0 );    // ! Enforced by wrapper.
 
714
        XMP_Status status;
 
715
        
 
716
        XMP_StringMapPos p2uEnd = sNamespacePrefixToURIMap->end();      // ! Move up to avoid gcc complaints.
 
717
        XMP_StringMapPos u2pEnd = sNamespaceURIToPrefixMap->end();
 
718
        
 
719
        status = DumpStringMap ( *sNamespacePrefixToURIMap, "Dumping namespace prefix to URI map", outProc, refCon );
 
720
        if ( status != 0 ) goto EXIT;
 
721
        
 
722
        if ( sNamespacePrefixToURIMap->size() != sNamespaceURIToPrefixMap->size() ) {
 
723
                OutProcLiteral ( "** bad namespace map sizes **" );
 
724
                XMP_Throw ( "Fatal namespace map problem", kXMPErr_InternalFailure );
 
725
        }
 
726
        
 
727
        for ( XMP_StringMapPos nsLeft = sNamespacePrefixToURIMap->begin(); nsLeft != p2uEnd; ++nsLeft ) {
 
728
 
 
729
                XMP_StringMapPos nsOther = sNamespaceURIToPrefixMap->find ( nsLeft->second );
 
730
                if ( (nsOther == u2pEnd) || (nsLeft != sNamespacePrefixToURIMap->find ( nsOther->second )) ) {
 
731
                        OutProcLiteral ( "  ** bad namespace URI **  " );
 
732
                        OutProcString ( nsLeft->second );
 
733
                        goto FAILURE;
 
734
                }
 
735
                
 
736
                for ( XMP_StringMapPos nsRight = nsLeft; nsRight != p2uEnd; ++nsRight ) {
 
737
                        if ( nsRight == nsLeft ) continue;      // ! Can't start at nsLeft+1, no operator+!
 
738
                        if ( nsLeft->second == nsRight->second ) {
 
739
                                OutProcLiteral ( "  ** duplicate namespace URI **  " );
 
740
                                OutProcString ( nsLeft->second );
 
741
                                goto FAILURE;
 
742
                        }
 
743
                }
 
744
 
 
745
        }
 
746
        
 
747
        for ( XMP_StringMapPos nsLeft = sNamespaceURIToPrefixMap->begin(); nsLeft != u2pEnd; ++nsLeft ) {
 
748
 
 
749
                XMP_StringMapPos nsOther = sNamespacePrefixToURIMap->find ( nsLeft->second );
 
750
                if ( (nsOther == p2uEnd) || (nsLeft != sNamespaceURIToPrefixMap->find ( nsOther->second )) ) {
 
751
                        OutProcLiteral ( "  ** bad namespace prefix **  " );
 
752
                        OutProcString ( nsLeft->second );
 
753
                        goto FAILURE;
 
754
                }
 
755
 
 
756
                for ( XMP_StringMapPos nsRight = nsLeft; nsRight != u2pEnd; ++nsRight ) {
 
757
                        if ( nsRight == nsLeft ) continue;      // ! Can't start at nsLeft+1, no operator+!
 
758
                        if ( nsLeft->second == nsRight->second ) {
 
759
                                OutProcLiteral ( "  ** duplicate namespace prefix **  " );
 
760
                                OutProcString ( nsLeft->second );
 
761
                                goto FAILURE;
 
762
                        }
 
763
                }
 
764
 
 
765
        }
 
766
 
 
767
EXIT:
 
768
        return status;
 
769
 
 
770
FAILURE:
 
771
        OutProcNewline();
 
772
        (void) DumpStringMap ( *sNamespaceURIToPrefixMap, "Dumping namespace URI to prefix map", outProc, refCon );
 
773
        XMP_Throw ( "Fatal namespace map problem", kXMPErr_InternalFailure );
 
774
        return 0;
 
775
        
 
776
}       // DumpNamespaces
 
777
 
 
778
 
 
779
// -------------------------------------------------------------------------------------------------
 
780
// DumpAliases
 
781
// -----------
 
782
 
 
783
/* class-static */ XMP_Status
 
784
XMPMeta::DumpAliases ( XMP_TextOutputProc outProc,
 
785
                       void *             refCon )
 
786
{
 
787
        XMP_Assert ( outProc != 0 );    // ! Enforced by wrapper.
 
788
        XMP_Status status;
 
789
 
 
790
        XMP_Assert ( sRegisteredAliasMap != 0 );
 
791
 
 
792
        XMP_cAliasMapPos aliasPos;
 
793
        XMP_cAliasMapPos aliasEnd = sRegisteredAliasMap->end();
 
794
        
 
795
        size_t maxLen = 0;
 
796
        for ( aliasPos = sRegisteredAliasMap->begin(); aliasPos != aliasEnd; ++aliasPos ) {
 
797
                size_t currLen = aliasPos->first.size();
 
798
                if ( currLen > maxLen ) maxLen = currLen;
 
799
        }
 
800
        
 
801
        OutProcLiteral ( "Dumping alias name to actual path map" );
 
802
        OutProcNewline();
 
803
                
 
804
        for ( aliasPos = sRegisteredAliasMap->begin(); aliasPos != aliasEnd; ++aliasPos ) {
 
805
 
 
806
                OutProcNChars ( "   ", 3 );
 
807
                OutProcString ( aliasPos->first );
 
808
                OutProcPadding ( maxLen - aliasPos->first.size() );
 
809
                OutProcNChars ( " => ", 4 );
 
810
 
 
811
                size_t actualPathSize = aliasPos->second.size();
 
812
                for ( size_t stepNum = 1; stepNum < actualPathSize; ++stepNum ) OutProcString ( aliasPos->second[stepNum].step );
 
813
 
 
814
                XMP_OptionBits arrayForm = aliasPos->second[1].options & kXMP_PropArrayFormMask;
 
815
 
 
816
                if ( arrayForm == 0 ) {
 
817
                        if ( actualPathSize != 2 ) OutProcLiteral ( "  ** bad actual path **" );
 
818
                } else {
 
819
                        OutProcNChars ( "  ", 2 );
 
820
                        DumpNodeOptions ( arrayForm, outProc, refCon );
 
821
                        if ( ! (arrayForm & kXMP_PropValueIsArray) ) OutProcLiteral ( "  ** bad array form **" );
 
822
                        if ( actualPathSize != 3 ) OutProcLiteral ( "  ** bad actual path **" );
 
823
                }
 
824
 
 
825
                if ( aliasPos->second[0].options != kXMP_SchemaNode ) OutProcLiteral ( "  ** bad schema form **" );
 
826
 
 
827
                OutProcNewline();
 
828
 
 
829
        }
 
830
        
 
831
EXIT:
 
832
        return status;
 
833
        
 
834
}       // DumpAliases
 
835
 
 
836
 
 
837
// -------------------------------------------------------------------------------------------------
 
838
// GetGlobalOptions
 
839
// ----------------
 
840
 
 
841
/* class-static */ XMP_OptionBits
 
842
XMPMeta::GetGlobalOptions()
 
843
{
 
844
        XMP_OptionBits  options = 0;
 
845
        
 
846
        return options;
 
847
        
 
848
}       // GetGlobalOptions
 
849
 
 
850
 
 
851
// -------------------------------------------------------------------------------------------------
 
852
// SetGlobalOptions
 
853
// ----------------
 
854
 
 
855
/* class-static */ void
 
856
XMPMeta::SetGlobalOptions ( XMP_OptionBits options )
 
857
{
 
858
 
 
859
        XMP_Throw ( "Unimplemented method XMPMeta::SetGlobalOptions", kXMPErr_Unimplemented );
 
860
                void * p; p = &options; // Avoid unused param warnings.
 
861
 
 
862
}       // SetGlobalOptions
 
863
 
 
864
 
 
865
// -------------------------------------------------------------------------------------------------
 
866
// RegisterNamespace
 
867
// -----------------
 
868
 
 
869
/* class-static */ bool
 
870
XMPMeta::RegisterNamespace ( XMP_StringPtr       namespaceURI,
 
871
                                                         XMP_StringPtr   suggestedPrefix,
 
872
                                                         XMP_StringPtr * registeredPrefix,
 
873
                                                         XMP_StringLen * prefixSize )
 
874
{
 
875
        bool    prefixMatches   = false;
 
876
        
 
877
        XMP_Assert ( (registeredPrefix != 0) && (prefixSize != 0) );            // ! Enforced by wrapper.
 
878
        if ( (*namespaceURI == 0) || (*suggestedPrefix == 0) ) {
 
879
                XMP_Throw ( "Empty namespace URI or prefix", kXMPErr_BadParam );
 
880
        }
 
881
        
 
882
        XMP_VarString   nsURI ( namespaceURI );
 
883
        XMP_VarString   suggPrefix ( suggestedPrefix );
 
884
        if ( suggPrefix[suggPrefix.size()-1] != ':' ) suggPrefix += ':';
 
885
        VerifySimpleXMLName ( suggestedPrefix, suggestedPrefix+suggPrefix.size()-1 );   // Exclude the colon.
 
886
        
 
887
        XMP_StringMapPos        uriPos  = sNamespaceURIToPrefixMap->find ( nsURI );
 
888
        
 
889
        if ( uriPos == sNamespaceURIToPrefixMap->end() ) {
 
890
                
 
891
                // The URI is not yet registered, make sure we use a unique prefix.
 
892
                
 
893
                XMP_VarString   uniqPrefix ( suggPrefix );
 
894
                int             suffix = 0;
 
895
                char    buffer [32];
 
896
 
 
897
                while ( true ) {
 
898
                        if ( sNamespacePrefixToURIMap->find ( uniqPrefix ) == sNamespacePrefixToURIMap->end() ) break;
 
899
                        ++suffix;
 
900
                        snprintf ( buffer, sizeof(buffer), "_%d_:", suffix );   // AUDIT: Using sizeof for snprintf length is safe.
 
901
                        uniqPrefix = suggPrefix;
 
902
                        uniqPrefix.erase ( uniqPrefix.size()-1 );       // ! Remove the trailing ':'.
 
903
                        uniqPrefix += buffer;
 
904
                }
 
905
                
 
906
                // Add the new namespace to both maps.
 
907
                
 
908
                XMP_StringPair  newNS ( nsURI, uniqPrefix );
 
909
                uriPos = sNamespaceURIToPrefixMap->insert ( sNamespaceURIToPrefixMap->end(), newNS );
 
910
                
 
911
                newNS.first.swap ( newNS.second );
 
912
                (void) sNamespacePrefixToURIMap->insert ( sNamespacePrefixToURIMap->end(), newNS );
 
913
 
 
914
        }
 
915
        
 
916
        // Return the actual prefix and see if it matches the suggested prefix.
 
917
        
 
918
        *registeredPrefix = uriPos->second.c_str();
 
919
        *prefixSize = uriPos->second.size();
 
920
        
 
921
        prefixMatches = ( uriPos->second == suggPrefix );
 
922
        return prefixMatches;
 
923
        
 
924
}       // RegisterNamespace
 
925
 
 
926
 
 
927
// -------------------------------------------------------------------------------------------------
 
928
// GetNamespacePrefix
 
929
// ------------------
 
930
 
 
931
/* class-static */ bool
 
932
XMPMeta::GetNamespacePrefix     ( XMP_StringPtr   namespaceURI,
 
933
                                                          XMP_StringPtr * namespacePrefix,
 
934
                                                          XMP_StringLen * prefixSize )
 
935
{
 
936
        bool    found   = false;
 
937
        
 
938
        XMP_Assert ( *namespaceURI != 0 );      // ! Enforced by wrapper.
 
939
        XMP_Assert ( (namespacePrefix != 0) && (prefixSize != 0) );     // ! Enforced by wrapper.
 
940
 
 
941
        XMP_VarString    nsURI ( namespaceURI );
 
942
        XMP_StringMapPos uriPos = sNamespaceURIToPrefixMap->find ( nsURI );
 
943
        
 
944
        if ( uriPos != sNamespaceURIToPrefixMap->end() ) {
 
945
                *namespacePrefix = uriPos->second.c_str();
 
946
                *prefixSize = uriPos->second.size();
 
947
                found = true;
 
948
        }
 
949
        
 
950
        return found;
 
951
        
 
952
}       // GetNamespacePrefix
 
953
 
 
954
 
 
955
// -------------------------------------------------------------------------------------------------
 
956
// GetNamespaceURI
 
957
// ---------------
 
958
 
 
959
/* class-static */ bool
 
960
XMPMeta::GetNamespaceURI ( XMP_StringPtr   namespacePrefix,
 
961
                                                   XMP_StringPtr * namespaceURI,
 
962
                                                   XMP_StringLen * uriSize )
 
963
{
 
964
        bool    found   = false;
 
965
        
 
966
        XMP_Assert ( *namespacePrefix != 0 );   // ! Enforced by wrapper.
 
967
        XMP_Assert ( (namespacePrefix != 0) && (namespaceURI != 0) );   // ! Enforced by wrapper.
 
968
 
 
969
        XMP_VarString nsPrefix ( namespacePrefix );
 
970
        if ( nsPrefix[nsPrefix.size()-1] != ':' ) nsPrefix += ':';
 
971
        
 
972
        XMP_StringMapPos prefixPos = sNamespacePrefixToURIMap->find ( nsPrefix );
 
973
        
 
974
        if ( prefixPos != sNamespacePrefixToURIMap->end() ) {
 
975
                *namespaceURI = prefixPos->second.c_str();
 
976
                *uriSize = prefixPos->second.size();
 
977
                found = true;
 
978
        }
 
979
        
 
980
        return found;
 
981
        
 
982
}       // GetNamespaceURI
 
983
 
 
984
 
 
985
// -------------------------------------------------------------------------------------------------
 
986
// DeleteNamespace
 
987
// ---------------
 
988
 
 
989
// *** Don't allow standard namespaces to be deleted.
 
990
 
 
991
/* class-static */ void
 
992
XMPMeta::DeleteNamespace ( XMP_StringPtr namespaceURI )
 
993
{
 
994
        namespaceURI = namespaceURI;    // Avoid unused parameter warning.
 
995
        XMP_Assert ( *namespaceURI != 0 );      // ! Enforced by wrapper.
 
996
        XMP_Throw ( "Unimplemented method XMPMeta::DeleteNamespace", kXMPErr_Unimplemented );   // *** #error "write me"
 
997
 
 
998
}       // DeleteNamespace
 
999
 
 
1000
 
 
1001
// -------------------------------------------------------------------------------------------------
 
1002
// RegisterAlias
 
1003
// -------------
 
1004
//
 
1005
// Allow 3 kinds of alias: 
 
1006
//      TopProp => TopProp
 
1007
//      TopProp => TopArray[1]
 
1008
//      TopProp => TopArray[@xml:lang='x-default']
 
1009
//
 
1010
// A new alias can be made to something that is already aliased, as long as the net result is one of
 
1011
// the legitimate forms. The new alias can already have aliases to it, also as long as result of
 
1012
// adjusting all of the exiting aliases leaves them legal.
 
1013
//
 
1014
// ! The caller assumes all risk that new aliases do not invalidate existing XMPMeta objects. Any
 
1015
// ! conflicts will result in later references throwing bad XPath exceptions.
 
1016
 
 
1017
/* class-static */ void
 
1018
XMPMeta::RegisterAlias ( XMP_StringPtr  aliasNS,
 
1019
                                                 XMP_StringPtr  aliasProp,
 
1020
                                                 XMP_StringPtr  actualNS,
 
1021
                                                 XMP_StringPtr  actualProp,
 
1022
                                                 XMP_OptionBits arrayForm )
 
1023
{
 
1024
        XMP_ExpandedXPath       expAlias, expActual;
 
1025
        XMP_AliasMapPos         mapPos;
 
1026
        XMP_ExpandedXPath *     regActual = 0;
 
1027
 
 
1028
        XMP_Assert ( (aliasNS != 0) && (aliasProp != 0) && (actualNS != 0) && (actualProp != 0) );      // Enforced by wrapper.
 
1029
                
 
1030
        // Expand the alias and actual names, make sure they are one of the basic 3 forms. When counting
 
1031
        // the expanded XPath size remember that the schema URI is the first component. We don't have to
 
1032
        // compare the schema URIs though, the (unique) prefix is part of the top property name.
 
1033
        
 
1034
        ExpandXPath ( aliasNS, aliasProp, &expAlias );
 
1035
        ExpandXPath ( actualNS, actualProp, &expActual );
 
1036
        if ( (expAlias.size() != 2) || (expActual.size() != 2) ) {
 
1037
                XMP_Throw ( "Alias and actual property names must be simple", kXMPErr_BadXPath );
 
1038
        }
 
1039
        
 
1040
        arrayForm = VerifySetOptions ( arrayForm, 0 );
 
1041
        if ( arrayForm != 0 ) {
 
1042
                if ( (arrayForm & ~kXMP_PropArrayFormMask) != 0 ) XMP_Throw ( "Only array form flags are allowed", kXMPErr_BadOptions );
 
1043
                expActual[1].options |= arrayForm;      // Set the array form for the top level step.
 
1044
                if ( ! (arrayForm & kXMP_PropArrayIsAltText) ) {
 
1045
                        expActual.push_back ( XPathStepInfo ( "[1]", kXMP_ArrayIndexStep ) );
 
1046
                } else {
 
1047
                        expActual.push_back ( XPathStepInfo ( "[?xml:lang=\"x-default\"]", kXMP_QualSelectorStep ) );
 
1048
                }
 
1049
        }
 
1050
 
 
1051
        // See if there are any conflicts with existing aliases. A couple of the checks are easy. If the
 
1052
        // alias is already aliased it is only OK to reregister an identical alias. If the actual is
 
1053
        // already aliased to something else and the new chain is legal, just swap in the old base.
 
1054
 
 
1055
        mapPos = sRegisteredAliasMap->find ( expAlias[kRootPropStep].step );
 
1056
        if ( mapPos != sRegisteredAliasMap->end() ) {
 
1057
 
 
1058
                // This alias is already registered to something, make sure it is the same something.
 
1059
 
 
1060
                regActual = &mapPos->second;
 
1061
                if ( arrayForm != (mapPos->second[1].options & kXMP_PropArrayFormMask) ) {
 
1062
                        XMP_Throw ( "Mismatch with existing alias array form", kXMPErr_BadParam );
 
1063
                }
 
1064
                if ( expActual.size() != regActual->size() ) {
 
1065
                        XMP_Throw ( "Mismatch with existing actual path", kXMPErr_BadParam );
 
1066
                }
 
1067
                if ( expActual[kRootPropStep].step != (*regActual)[kRootPropStep].step ) {
 
1068
                        XMP_Throw ( "Mismatch with existing actual name", kXMPErr_BadParam );
 
1069
                }
 
1070
                if ( (expActual.size() == 3) && (expActual[kAliasIndexStep].step != (*regActual)[kAliasIndexStep].step) ) {
 
1071
                        XMP_Throw ( "Mismatch with existing actual array item", kXMPErr_BadParam );
 
1072
                }
 
1073
                return;
 
1074
 
 
1075
        }
 
1076
 
 
1077
        mapPos = sRegisteredAliasMap->find ( expActual[kRootPropStep].step );
 
1078
        if ( mapPos != sRegisteredAliasMap->end() ) {
 
1079
        
 
1080
                // The actual is already aliased to something else.
 
1081
                
 
1082
                regActual = &mapPos->second;
 
1083
                if ( expActual.size() == 2 ) {
 
1084
                        expActual = *regActual; // TopProp => TopProp => anything : substitute the entire old base.
 
1085
                } else if ( regActual->size() != 2 ) {
 
1086
                        XMP_Throw ( "Can't alias an array item to an array item", kXMPErr_BadParam );   // TopProp => TopArray[] => TopArray[] : nope.
 
1087
                } else {
 
1088
                        expActual[kSchemaStep].step = (*regActual)[kSchemaStep].step;           // TopProp => TopArray[] => TopProp :
 
1089
                        expActual[kRootPropStep].step   = (*regActual)[kRootPropStep].step;     //      substitute the old base name.
 
1090
                }
 
1091
        
 
1092
        }
 
1093
        
 
1094
        // Checking for existing aliases to this one is touchier. This involves updating the alias map,
 
1095
        // which must not be done unless all of the changes are legal. So we need 2 loops, one to verify
 
1096
        // that everything is OK, and one to make the changes. The bad case is:
 
1097
        //      TopProp => TopArray[] => TopArray[]
 
1098
        // In the valid cases we back substitute the new base.
 
1099
        
 
1100
        for ( mapPos = sRegisteredAliasMap->begin(); mapPos != sRegisteredAliasMap->end(); ++mapPos ) {
 
1101
                regActual = &mapPos->second;
 
1102
                if ( expAlias[kRootPropStep].step == (*regActual)[kRootPropStep].step ) {
 
1103
                        if ( (regActual->size() == 2) && (expAlias.size() == 2) ) {
 
1104
                                XMP_Throw ( "Can't alias an array item to an array item", kXMPErr_BadParam );
 
1105
                        }
 
1106
                }
 
1107
        }
 
1108
        
 
1109
        for ( mapPos = sRegisteredAliasMap->begin(); mapPos != sRegisteredAliasMap->end(); ++mapPos ) {
 
1110
                regActual = &mapPos->second;
 
1111
                if ( expAlias[kRootPropStep].step == (*regActual)[kRootPropStep].step ) {
 
1112
 
 
1113
                        if ( regActual->size() == 1 ) {
 
1114
                                *regActual = expActual; // TopProp => TopProp => anything : substitute the entire new base.
 
1115
                        } else {
 
1116
                                (*regActual)[kSchemaStep].step = expActual[kSchemaStep].step;   // TopProp => TopArray[] => TopProp :
 
1117
                                (*regActual)[kRootPropStep].step   = expActual[kRootPropStep].step;             //      substitute the new base name.
 
1118
                        }
 
1119
 
 
1120
                }
 
1121
        }
 
1122
        
 
1123
        // Finally, all is OK to register the new alias.
 
1124
        
 
1125
        (void) sRegisteredAliasMap->insert ( XMP_AliasMap::value_type ( expAlias[kRootPropStep].step, expActual ) );
 
1126
 
 
1127
}       // RegisterAlias
 
1128
 
 
1129
 
 
1130
// -------------------------------------------------------------------------------------------------
 
1131
// ResolveAlias
 
1132
// ------------
 
1133
 
 
1134
/* class-static */ bool
 
1135
XMPMeta::ResolveAlias ( XMP_StringPtr    aliasNS,
 
1136
                                                XMP_StringPtr    aliasProp,
 
1137
                                                XMP_StringPtr *  actualNS,
 
1138
                                                XMP_StringLen *  nsSize,
 
1139
                                                XMP_StringPtr *  actualProp,
 
1140
                                                XMP_StringLen *  propSize,
 
1141
                                                XMP_OptionBits * arrayForm )
 
1142
{
 
1143
        XMP_Assert ( (aliasNS != 0) && (aliasProp != 0) );      // Enforced by wrapper.
 
1144
        XMP_Assert ( (actualNS != 0) && (nsSize != 0) && (actualProp != 0) && (propSize != 0) && (arrayForm != 0) );    // Enforced by wrapper.
 
1145
        
 
1146
        // Expand the input path and look up the first component in the alias table. Return if not an alias.
 
1147
        
 
1148
        XMP_ExpandedXPath fullPath, minPath;
 
1149
        ExpandXPath ( aliasNS, aliasProp, &fullPath );
 
1150
        XMP_Assert ( fullPath.size() >= 2 );
 
1151
 
 
1152
        minPath.push_back ( fullPath[kSchemaStep] );
 
1153
        minPath.push_back ( fullPath[kRootPropStep] );
 
1154
        XMP_AliasMapPos mapPos = sRegisteredAliasMap->find ( minPath[kRootPropStep].step );
 
1155
        if ( mapPos == sRegisteredAliasMap->end() ) return false;
 
1156
        
 
1157
        // Replace the alias portion of the full expanded path. Compose the output path string.
 
1158
        
 
1159
        const XMP_ExpandedXPath & actualPath = mapPos->second;
 
1160
        
 
1161
        fullPath[kSchemaStep] = actualPath[kSchemaStep];
 
1162
        fullPath[kRootPropStep] = actualPath[kRootPropStep];
 
1163
        if ( actualPath.size() > 2 ) {  // This is an alias to an array item.
 
1164
                XMP_ExpandedXPathPos insertPos = fullPath.begin() + kAliasIndexStep;
 
1165
                fullPath.insert ( insertPos, actualPath[kAliasIndexStep] );
 
1166
        }       
 
1167
        
 
1168
        *sOutputNS = fullPath[kSchemaStep].step;
 
1169
        *actualNS  = sOutputNS->c_str();
 
1170
        *nsSize    = sOutputNS->size();
 
1171
        
 
1172
        ComposeXPath ( fullPath, sOutputStr );
 
1173
        *actualProp = sOutputStr->c_str();
 
1174
        *propSize   = sOutputStr->size();
 
1175
 
 
1176
        *arrayForm  = actualPath[kRootPropStep].options & kXMP_PropArrayFormMask;
 
1177
        
 
1178
        #if XMP_DebugBuild      // Test that the output string is valid and unchanged by round trip expand/compose.
 
1179
                XMP_ExpandedXPath rtPath;
 
1180
                ExpandXPath ( *actualNS, *actualProp, &rtPath );
 
1181
                std::string rtString;
 
1182
                ComposeXPath ( rtPath, &rtString );
 
1183
                XMP_Assert ( rtString == *sOutputStr );
 
1184
        #endif
 
1185
        
 
1186
        return true;
 
1187
        
 
1188
}       // ResolveAlias
 
1189
 
 
1190
 
 
1191
// -------------------------------------------------------------------------------------------------
 
1192
// DeleteAlias
 
1193
// -----------
 
1194
 
 
1195
/* class-static */ void
 
1196
XMPMeta::DeleteAlias ( XMP_StringPtr aliasNS,
 
1197
                                           XMP_StringPtr aliasProp )
 
1198
{
 
1199
 
 
1200
        XMP_Assert ( (aliasNS != 0) && (aliasProp != 0) );      // Enforced by wrapper.
 
1201
        XMP_Throw ( "Unimplemented method XMPMeta::DeleteAlias", kXMPErr_Unimplemented );   // *** #error "write me"
 
1202
                void * p; p = &aliasNS; p = &aliasProp; // Avoid unused param warnings.
 
1203
 
 
1204
}       // DeleteAlias
 
1205
 
 
1206
 
 
1207
// -------------------------------------------------------------------------------------------------
 
1208
// RegisterStandardAliases
 
1209
// -----------------------
 
1210
 
 
1211
/* class-static */ void
 
1212
XMPMeta::RegisterStandardAliases ( XMP_StringPtr schemaNS )
 
1213
{
 
1214
        XMP_Assert ( schemaNS != 0 );   // Enforced by wrapper.
 
1215
 
 
1216
        const bool doAll = (*schemaNS == 0);
 
1217
        
 
1218
        if ( doAll || XMP_LitMatch ( schemaNS, kXMP_NS_XMP ) ) {
 
1219
                // Aliases from XMP to DC.
 
1220
            XMPMeta::RegisterAlias ( kXMP_NS_XMP, "Author",       kXMP_NS_DC, "creator", kXMP_PropArrayIsOrdered );
 
1221
            XMPMeta::RegisterAlias ( kXMP_NS_XMP, "Authors",      kXMP_NS_DC, "creator", 0 );
 
1222
            XMPMeta::RegisterAlias ( kXMP_NS_XMP, "Description",  kXMP_NS_DC, "description", 0 );
 
1223
            XMPMeta::RegisterAlias ( kXMP_NS_XMP, "Format",       kXMP_NS_DC, "format", 0 );
 
1224
            XMPMeta::RegisterAlias ( kXMP_NS_XMP, "Keywords",     kXMP_NS_DC, "subject", 0 );
 
1225
            XMPMeta::RegisterAlias ( kXMP_NS_XMP, "Locale",       kXMP_NS_DC, "language", 0 );
 
1226
            XMPMeta::RegisterAlias ( kXMP_NS_XMP, "Title",        kXMP_NS_DC, "title", 0 );
 
1227
            XMPMeta::RegisterAlias ( kXMP_NS_XMP_Rights, "Copyright", kXMP_NS_DC, "rights", 0 );
 
1228
        }
 
1229
 
 
1230
        if ( doAll || XMP_LitMatch ( schemaNS, kXMP_NS_PDF ) ) {
 
1231
                // Aliases from PDF to DC and XMP.
 
1232
            XMPMeta::RegisterAlias ( kXMP_NS_PDF, "Author",       kXMP_NS_DC, "creator", kXMP_PropArrayIsOrdered );
 
1233
            XMPMeta::RegisterAlias ( kXMP_NS_PDF, "BaseURL",      kXMP_NS_XMP, "BaseURL", 0 );
 
1234
            XMPMeta::RegisterAlias ( kXMP_NS_PDF, "CreationDate", kXMP_NS_XMP, "CreateDate", 0 );
 
1235
            XMPMeta::RegisterAlias ( kXMP_NS_PDF, "Creator",      kXMP_NS_XMP, "CreatorTool", 0 );
 
1236
            XMPMeta::RegisterAlias ( kXMP_NS_PDF, "ModDate",      kXMP_NS_XMP, "ModifyDate", 0 );
 
1237
            XMPMeta::RegisterAlias ( kXMP_NS_PDF, "Subject",      kXMP_NS_DC,  "description", kXMP_PropArrayIsAltText );
 
1238
            XMPMeta::RegisterAlias ( kXMP_NS_PDF, "Title",        kXMP_NS_DC,  "title", kXMP_PropArrayIsAltText );
 
1239
    }
 
1240
    
 
1241
        if ( doAll || XMP_LitMatch ( schemaNS, kXMP_NS_Photoshop ) ) {
 
1242
            // Aliases from PHOTOSHOP to DC and XMP.
 
1243
            XMPMeta::RegisterAlias ( kXMP_NS_Photoshop, "Author",       kXMP_NS_DC, "creator", kXMP_PropArrayIsOrdered );
 
1244
            XMPMeta::RegisterAlias ( kXMP_NS_Photoshop, "Caption",      kXMP_NS_DC, "description", kXMP_PropArrayIsAltText );
 
1245
            XMPMeta::RegisterAlias ( kXMP_NS_Photoshop, "Copyright",    kXMP_NS_DC, "rights", kXMP_PropArrayIsAltText );
 
1246
            XMPMeta::RegisterAlias ( kXMP_NS_Photoshop, "Keywords",     kXMP_NS_DC, "subject", 0 );
 
1247
            XMPMeta::RegisterAlias ( kXMP_NS_Photoshop, "Marked",       kXMP_NS_XMP_Rights, "Marked", 0 );
 
1248
            XMPMeta::RegisterAlias ( kXMP_NS_Photoshop, "Title",        kXMP_NS_DC, "title", kXMP_PropArrayIsAltText );
 
1249
            XMPMeta::RegisterAlias ( kXMP_NS_Photoshop, "WebStatement", kXMP_NS_XMP_Rights, "WebStatement", 0 );
 
1250
    }
 
1251
            
 
1252
        if ( doAll || XMP_LitMatch ( schemaNS, kXMP_NS_TIFF ) || XMP_LitMatch ( schemaNS, kXMP_NS_EXIF ) ) {
 
1253
            // Aliases from TIFF and EXIF to DC and XMP.
 
1254
                XMPMeta::RegisterAlias ( kXMP_NS_TIFF, "Artist",           kXMP_NS_DC,  "creator", kXMP_PropArrayIsOrdered);
 
1255
            XMPMeta::RegisterAlias ( kXMP_NS_TIFF, "Copyright",        kXMP_NS_DC,  "rights", 0 );
 
1256
            XMPMeta::RegisterAlias ( kXMP_NS_TIFF, "DateTime",         kXMP_NS_XMP, "ModifyDate", 0 );
 
1257
            XMPMeta::RegisterAlias ( kXMP_NS_TIFF, "ImageDescription", kXMP_NS_DC,  "description", 0 );
 
1258
            XMPMeta::RegisterAlias ( kXMP_NS_TIFF, "Software",         kXMP_NS_XMP, "CreatorTool", 0 );
 
1259
    }
 
1260
    
 
1261
        if ( doAll || XMP_LitMatch ( schemaNS, kXMP_NS_PNG ) ) {        // ! From Acrobat ImageCapture:
 
1262
                XMPMeta::RegisterAlias ( kXMP_NS_PNG, "Author",           kXMP_NS_DC,  "creator", kXMP_PropArrayIsOrdered);
 
1263
                XMPMeta::RegisterAlias ( kXMP_NS_PNG, "Copyright",        kXMP_NS_DC,  "rights", kXMP_PropArrayIsAltText);
 
1264
                XMPMeta::RegisterAlias ( kXMP_NS_PNG, "CreationTime",     kXMP_NS_XMP, "CreateDate", 0 );
 
1265
                XMPMeta::RegisterAlias ( kXMP_NS_PNG, "Description",      kXMP_NS_DC,  "description", kXMP_PropArrayIsAltText);
 
1266
                XMPMeta::RegisterAlias ( kXMP_NS_PNG, "ModificationTime", kXMP_NS_XMP, "ModifyDate", 0 );
 
1267
                XMPMeta::RegisterAlias ( kXMP_NS_PNG, "Software",         kXMP_NS_XMP, "CreatorTool", 0 );
 
1268
                XMPMeta::RegisterAlias ( kXMP_NS_PNG, "Title",            kXMP_NS_DC,  "title", kXMP_PropArrayIsAltText);
 
1269
        }
 
1270
 
 
1271
}       // RegisterStandardAliases
 
1272
 
 
1273
 
 
1274
// =================================================================================================
 
1275
// Class Methods
 
1276
// =============
 
1277
//
 
1278
//
 
1279
// =================================================================================================
 
1280
 
 
1281
 
 
1282
// -------------------------------------------------------------------------------------------------
 
1283
// DumpObject
 
1284
// ----------
 
1285
 
 
1286
XMP_Status
 
1287
XMPMeta::DumpObject ( XMP_TextOutputProc outProc,
 
1288
                      void *             refCon ) const
 
1289
{
 
1290
        XMP_Assert ( outProc != 0 );    // ! Enforced by wrapper.
 
1291
        XMP_Status status;
 
1292
        
 
1293
        OutProcLiteral ( "Dumping XMPMeta object \"" );
 
1294
        OutProcString ( tree.name );
 
1295
        OutProcNChars ( "\"  ", 3 );
 
1296
        status = DumpNodeOptions ( tree.options, outProc, refCon );
 
1297
        if ( status != 0 ) goto EXIT;
 
1298
        #if 0   // *** XMP_DebugBuild
 
1299
                if ( (tree._namePtr != tree.name.c_str()) ||
 
1300
                         (tree._valuePtr != tree.value.c_str()) ) OutProcLiteral ( "  ** bad debug string **" );
 
1301
        #endif
 
1302
        OutProcNewline();
 
1303
        
 
1304
        if ( ! tree.value.empty() ) {
 
1305
                OutProcLiteral ( "** bad root value **  \"" );
 
1306
                OutProcString ( tree.value );
 
1307
                OutProcNChars ( "\"", 1 );
 
1308
                OutProcNewline();
 
1309
        }
 
1310
 
 
1311
        if ( ! tree.qualifiers.empty() ) {
 
1312
                OutProcLiteral ( "** bad root qualifiers **" );
 
1313
                OutProcNewline();
 
1314
                for ( size_t qualNum = 0, qualLim = tree.qualifiers.size(); qualNum < qualLim; ++qualNum ) {
 
1315
                        status = DumpPropertyTree ( tree.qualifiers[qualNum], 3, 0, outProc, refCon );
 
1316
                }
 
1317
        }
 
1318
 
 
1319
        if ( ! tree.children.empty() ) {
 
1320
 
 
1321
                for ( size_t childNum = 0, childLim = tree.children.size(); childNum < childLim; ++childNum ) {
 
1322
 
 
1323
                        const XMP_Node * currSchema = tree.children[childNum];
 
1324
 
 
1325
                        OutProcNewline();
 
1326
                        OutProcIndent ( 1 );
 
1327
                        OutProcString ( currSchema->name );
 
1328
                        OutProcNChars ( "  ", 2 );
 
1329
                        OutProcString ( currSchema->value );
 
1330
                        OutProcNChars ( "  ", 2 );
 
1331
                        status = DumpNodeOptions ( currSchema->options, outProc, refCon );
 
1332
                        if ( status != 0 ) goto EXIT;
 
1333
                        #if 0   // *** XMP_DebugBuild
 
1334
                                if ( (currSchema->_namePtr != currSchema->name.c_str()) ||
 
1335
                                         (currSchema->_valuePtr != currSchema->value.c_str()) ) OutProcLiteral ( "  ** bad debug string **" );
 
1336
                        #endif
 
1337
                        OutProcNewline();
 
1338
 
 
1339
                        if ( ! (currSchema->options & kXMP_SchemaNode) ) {
 
1340
                                OutProcLiteral ( "** bad schema options **" );
 
1341
                                OutProcNewline();
 
1342
                        }
 
1343
 
 
1344
                        if ( ! currSchema->qualifiers.empty() ) {
 
1345
                                OutProcLiteral ( "** bad schema qualifiers **" );
 
1346
                                OutProcNewline();
 
1347
                                for ( size_t qualNum = 0, qualLim = currSchema->qualifiers.size(); qualNum < qualLim; ++qualNum ) {
 
1348
                                        DumpPropertyTree ( currSchema->qualifiers[qualNum], 3, 0, outProc, refCon );
 
1349
                                }
 
1350
                        }
 
1351
 
 
1352
                        for ( size_t childNum = 0, childLim = currSchema->children.size(); childNum < childLim; ++childNum ) {
 
1353
                                DumpPropertyTree ( currSchema->children[childNum], 2, 0, outProc, refCon );
 
1354
                        }
 
1355
 
 
1356
                }
 
1357
 
 
1358
        }
 
1359
        
 
1360
EXIT:
 
1361
        return status;
 
1362
 
 
1363
}       // DumpObject
 
1364
 
 
1365
 
 
1366
// -------------------------------------------------------------------------------------------------
 
1367
// CountArrayItems
 
1368
// ---------------
 
1369
 
 
1370
XMP_Index
 
1371
XMPMeta::CountArrayItems ( XMP_StringPtr schemaNS,
 
1372
                                                   XMP_StringPtr arrayName ) const
 
1373
{
 
1374
        XMP_Assert ( (schemaNS != 0) && (arrayName != 0) );     // Enforced by wrapper.
 
1375
 
 
1376
        XMP_ExpandedXPath       expPath;
 
1377
        ExpandXPath ( schemaNS, arrayName, &expPath );
 
1378
        
 
1379
        const XMP_Node * arrayNode = FindConstNode ( &tree, expPath );
 
1380
 
 
1381
        if ( arrayNode == 0 ) return 0;
 
1382
        if ( ! (arrayNode->options & kXMP_PropValueIsArray) ) XMP_Throw ( "The named property is not an array", kXMPErr_BadXPath );
 
1383
        return arrayNode->children.size();
 
1384
        
 
1385
}       // CountArrayItems
 
1386
 
 
1387
 
 
1388
// -------------------------------------------------------------------------------------------------
 
1389
// GetObjectName
 
1390
// -------------
 
1391
 
 
1392
void
 
1393
XMPMeta::GetObjectName ( XMP_StringPtr * namePtr,
 
1394
                                                 XMP_StringLen * nameLen ) const
 
1395
{
 
1396
        
 
1397
        *namePtr = tree.name.c_str();
 
1398
        *nameLen = tree.name.size();
 
1399
        
 
1400
}       // GetObjectName
 
1401
 
 
1402
 
 
1403
// -------------------------------------------------------------------------------------------------
 
1404
// SetObjectName
 
1405
// -------------
 
1406
 
 
1407
void
 
1408
XMPMeta::SetObjectName ( XMP_StringPtr name )
 
1409
{
 
1410
        VerifyUTF8 ( name );    // Throws if the string is not legit UTF-8.
 
1411
        tree.name = name;
 
1412
 
 
1413
}       // SetObjectName
 
1414
 
 
1415
 
 
1416
// -------------------------------------------------------------------------------------------------
 
1417
// GetObjectOptions
 
1418
// ----------------
 
1419
 
 
1420
XMP_OptionBits
 
1421
XMPMeta::GetObjectOptions() const
 
1422
{
 
1423
        XMP_OptionBits  options = 0;
 
1424
        
 
1425
        return options;
 
1426
        
 
1427
}       // GetObjectOptions
 
1428
 
 
1429
 
 
1430
// -------------------------------------------------------------------------------------------------
 
1431
// SetObjectOptions
 
1432
// ----------------
 
1433
 
 
1434
void
 
1435
XMPMeta::SetObjectOptions ( XMP_OptionBits options )
 
1436
{
 
1437
 
 
1438
        XMP_Throw ( "Unimplemented method XMPMeta::SetObjectOptions", kXMPErr_Unimplemented );
 
1439
                void * p; p = &options; // Avoid unused param warnings.
 
1440
 
 
1441
}       // SetObjectOptions
 
1442
 
 
1443
 
 
1444
// -------------------------------------------------------------------------------------------------
 
1445
// Clone
 
1446
// -----
 
1447
 
 
1448
XMPMeta *
 
1449
XMPMeta::Clone ( XMP_OptionBits options ) const
 
1450
{
 
1451
        if ( options != 0 ) XMP_Throw ( "No options are defined yet", kXMPErr_BadOptions );
 
1452
        
 
1453
        XMPMeta * clone = new XMPMeta;
 
1454
        
 
1455
        clone->tree.options = this->tree.options;
 
1456
        clone->tree.name    = this->tree.name;
 
1457
        clone->tree.value   = this->tree.value;
 
1458
        XMP_Assert ( this->tree.parent == 0 );
 
1459
 
 
1460
        #if 0   // *** XMP_DebugBuild
 
1461
                clone->tree._namePtr = clone->tree.name.c_str();
 
1462
                clone->tree._valuePtr = clone->tree.value.c_str();
 
1463
        #endif
 
1464
        
 
1465
        CloneOffspring ( &this->tree, &clone->tree );
 
1466
        
 
1467
        XMP_Assert ( clone->clientRefs == 0 );  // Gets incremneted later.
 
1468
        return clone;
 
1469
        
 
1470
}       // Clone
 
1471
 
 
1472
// =================================================================================================