Coverage Report

Created: 2025-08-29 06:13

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