Coverage Report

Created: 2026-04-29 07:00

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/exiv2/xmpsdk/src/ParseRDF.cpp
Line
Count
Source
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
9
#include "XMP_Environment.h"  // ! This must be the first include!
10
#include "XMPCore_Impl.hpp"
11
#include "ExpatAdapter.hpp"
12
13
#include <cstring>
14
15
#if DEBUG
16
  #include <iostream>
17
#endif
18
19
using namespace std;
20
21
#if XMP_WinBuild
22
#   ifdef _MSC_VER
23
        #pragma warning ( disable : 4189 )  // local variable is initialized but not referenced
24
        #pragma warning ( disable : 4505 )  // unreferenced local function has been removed
25
#   endif
26
#endif
27
28
// =================================================================================================
29
30
// *** This might be faster and use less memory as a state machine. A big advantage of building an
31
// *** XML tree though is easy lookahead during the recursive descent processing.
32
33
// *** It would be nice to give a line number or byte offset in the exception messages.
34
35
36
// 7 RDF/XML Grammar (from http://www.w3.org/TR/rdf-syntax-grammar/#section-Infoset-Grammar)
37
//
38
// 7.1 Grammar summary
39
//
40
// 7.2.2 coreSyntaxTerms
41
//    rdf:RDF | rdf:ID | rdf:about | rdf:parseType | rdf:resource | rdf:nodeID | rdf:datatype
42
//
43
// 7.2.3 syntaxTerms
44
//    coreSyntaxTerms | rdf:Description | rdf:li
45
//
46
// 7.2.4 oldTerms
47
//    rdf:aboutEach | rdf:aboutEachPrefix | rdf:bagID
48
//
49
// 7.2.5 nodeElementURIs
50
//    anyURI - ( coreSyntaxTerms | rdf:li | oldTerms )
51
//
52
// 7.2.6 propertyElementURIs
53
//    anyURI - ( coreSyntaxTerms | rdf:Description | oldTerms )
54
//
55
// 7.2.7 propertyAttributeURIs
56
//    anyURI - ( coreSyntaxTerms | rdf:Description | rdf:li | oldTerms )
57
//
58
// 7.2.8 doc
59
//    root ( document-element == RDF, children == list ( RDF ) )
60
//
61
// 7.2.9 RDF
62
//    start-element ( URI == rdf:RDF, attributes == set() )
63
//    nodeElementList
64
//    end-element()
65
//
66
// 7.2.10 nodeElementList
67
//    ws* ( nodeElement ws* )*
68
//
69
// 7.2.11 nodeElement
70
//    start-element ( URI == nodeElementURIs,
71
//            attributes == set ( ( idAttr | nodeIdAttr | aboutAttr )?, propertyAttr* ) )
72
//    propertyEltList
73
//    end-element()
74
//
75
// 7.2.12 ws
76
//    A text event matching white space defined by [XML] definition White Space Rule [3] S in section Common Syntactic Constructs.
77
//
78
// 7.2.13 propertyEltList
79
//    ws* ( propertyElt ws* )*
80
//
81
// 7.2.14 propertyElt
82
//    resourcePropertyElt | literalPropertyElt | parseTypeLiteralPropertyElt |
83
//    parseTypeResourcePropertyElt | parseTypeCollectionPropertyElt | parseTypeOtherPropertyElt | emptyPropertyElt
84
//
85
// 7.2.15 resourcePropertyElt
86
//    start-element ( URI == propertyElementURIs, attributes == set ( idAttr? ) )
87
//    ws* nodeElement ws*
88
//    end-element()
89
//
90
// 7.2.16 literalPropertyElt
91
//    start-element ( URI == propertyElementURIs, attributes == set ( idAttr?, datatypeAttr?) )
92
//    text()
93
//    end-element()
94
//
95
// 7.2.17 parseTypeLiteralPropertyElt
96
//    start-element ( URI == propertyElementURIs, attributes == set ( idAttr?, parseLiteral ) )
97
//    literal
98
//    end-element()
99
//
100
// 7.2.18 parseTypeResourcePropertyElt
101
//    start-element ( URI == propertyElementURIs, attributes == set ( idAttr?, parseResource ) )
102
//    propertyEltList
103
//    end-element()
104
//
105
// 7.2.19 parseTypeCollectionPropertyElt
106
//    start-element ( URI == propertyElementURIs, attributes == set ( idAttr?, parseCollection ) )
107
//    nodeElementList
108
//    end-element()
109
//
110
// 7.2.20 parseTypeOtherPropertyElt
111
//    start-element ( URI == propertyElementURIs, attributes == set ( idAttr?, parseOther ) )
112
//    propertyEltList
113
//    end-element()
114
//
115
// 7.2.21 emptyPropertyElt
116
//    start-element ( URI == propertyElementURIs,
117
//            attributes == set ( idAttr?, ( resourceAttr | nodeIdAttr )?, propertyAttr* ) )
118
//    end-element()
119
//
120
// 7.2.22 idAttr
121
//    attribute ( URI == rdf:ID, string-value == rdf-id )
122
//
123
// 7.2.23 nodeIdAttr
124
//    attribute ( URI == rdf:nodeID, string-value == rdf-id )
125
//
126
// 7.2.24 aboutAttr
127
//    attribute ( URI == rdf:about, string-value == URI-reference )
128
//
129
// 7.2.25 propertyAttr
130
//    attribute ( URI == propertyAttributeURIs, string-value == anyString )
131
//
132
// 7.2.26 resourceAttr
133
//    attribute ( URI == rdf:resource, string-value == URI-reference )
134
//
135
// 7.2.27 datatypeAttr
136
//    attribute ( URI == rdf:datatype, string-value == URI-reference )
137
//
138
// 7.2.28 parseLiteral
139
//    attribute ( URI == rdf:parseType, string-value == "Literal")
140
//
141
// 7.2.29 parseResource
142
//    attribute ( URI == rdf:parseType, string-value == "Resource")
143
//
144
// 7.2.30 parseCollection
145
//    attribute ( URI == rdf:parseType, string-value == "Collection")
146
//
147
// 7.2.31 parseOther
148
//    attribute ( URI == rdf:parseType, string-value == anyString - ("Resource" | "Literal" | "Collection") )
149
//
150
// 7.2.32 URI-reference
151
//    An RDF URI Reference.
152
//
153
// 7.2.33 literal
154
//    Any XML element content that is allowed according to [XML] definition Content of Elements Rule [43] content
155
//    in section 3.1 Start-Tags, End-Tags, and Empty-Element Tags.
156
//
157
// 7.2.34 rdf-id
158
//    An attribute string-value matching any legal [XML-NS] token NCName.
159
160
161
// =================================================================================================
162
// Primary Parsing Functions
163
// =========================
164
//
165
// Each of these is responsible for recognizing an RDF syntax production and adding the appropriate
166
// structure to the XMP tree. They simply return for success, failures will throw an exception.
167
168
static void
169
RDF_RDF ( XMP_Node * xmpTree, const XML_Node & xmlNode );
170
171
static void
172
RDF_NodeElementList ( XMP_Node * xmpParent, const XML_Node & xmlParent, bool isTopLevel );
173
174
static void
175
RDF_NodeElement ( XMP_Node * xmpParent, const XML_Node & xmlNode, bool isTopLevel );
176
177
static void
178
RDF_NodeElementAttrs ( XMP_Node * xmpParent, const XML_Node & xmlNode, bool isTopLevel );
179
180
static void
181
RDF_PropertyElementList ( XMP_Node * xmpParent, const XML_Node & xmlParent, bool isTopLevel );
182
enum { kIsTopLevel = true, kNotTopLevel = false };
183
184
static void
185
RDF_PropertyElement ( XMP_Node * xmpParent, const XML_Node & xmlNode, bool isTopLevel );
186
187
static void
188
RDF_ResourcePropertyElement ( XMP_Node * xmpParent, const XML_Node & xmlNode, bool isTopLevel );
189
190
static void
191
RDF_LiteralPropertyElement ( XMP_Node * xmpParent, const XML_Node & xmlNode, bool isTopLevel );
192
193
static void
194
RDF_ParseTypeLiteralPropertyElement ( XMP_Node * xmpParent, const XML_Node & xmlNode, bool isTopLevel );
195
196
static void
197
RDF_ParseTypeResourcePropertyElement ( XMP_Node * xmpParent, const XML_Node & xmlNode, bool isTopLevel );
198
199
static void
200
RDF_ParseTypeCollectionPropertyElement ( XMP_Node * xmpParent, const XML_Node & xmlNode, bool isTopLevel );
201
202
static void
203
RDF_ParseTypeOtherPropertyElement ( XMP_Node * xmpParent, const XML_Node & xmlNode, bool isTopLevel );
204
205
static void
206
RDF_EmptyPropertyElement ( XMP_Node * xmpParent, const XML_Node & xmlNode, bool isTopLevel );
207
208
209
// =================================================================================================
210
211
typedef XMP_Uns8 RDFTermKind;
212
213
// *** Logic might be safer with just masks.
214
215
enum {
216
  kRDFTerm_Other        =  0,
217
  kRDFTerm_RDF        =  1, // Start of coreSyntaxTerms.
218
  kRDFTerm_ID         =  2,
219
  kRDFTerm_about        =  3,
220
  kRDFTerm_parseType      =  4,
221
  kRDFTerm_resource     =  5,
222
  kRDFTerm_nodeID       =  6,
223
  kRDFTerm_datatype     =  7, // End of coreSyntaxTerms.
224
  kRDFTerm_Description    =  8, // Start of additions for syntaxTerms.
225
  kRDFTerm_li         =  9, // End of of additions for syntaxTerms.
226
  kRDFTerm_aboutEach      = 10, // Start of oldTerms.
227
  kRDFTerm_aboutEachPrefix  = 11,
228
  kRDFTerm_bagID        = 12, // End of oldTerms.
229
  
230
  kRDFTerm_FirstCore          = kRDFTerm_RDF,
231
  kRDFTerm_LastCore           = kRDFTerm_datatype,
232
  kRDFTerm_FirstSyntax        = kRDFTerm_FirstCore, // ! Yes, the syntax terms include the core terms.
233
  kRDFTerm_LastSyntax         = kRDFTerm_li,
234
  kRDFTerm_FirstOld           = kRDFTerm_aboutEach,
235
  kRDFTerm_LastOld            = kRDFTerm_bagID
236
};
237
238
enum {
239
  kRDFMask_Other        = 1 << kRDFTerm_Other,
240
  kRDFMask_RDF        = 1 << kRDFTerm_RDF,
241
  kRDFMask_ID         = 1 << kRDFTerm_ID,
242
  kRDFMask_about        = 1 << kRDFTerm_about,
243
  kRDFMask_parseType      = 1 << kRDFTerm_parseType,
244
  kRDFMask_resource     = 1 << kRDFTerm_resource,
245
  kRDFMask_nodeID       = 1 << kRDFTerm_nodeID,
246
  kRDFMask_datatype     = 1 << kRDFTerm_datatype,
247
  kRDFMask_Description    = 1 << kRDFTerm_Description,
248
  kRDFMask_li         = 1 << kRDFTerm_li,
249
  kRDFMask_aboutEach      = 1 << kRDFTerm_aboutEach,
250
  kRDFMask_aboutEachPrefix  = 1 << kRDFTerm_aboutEachPrefix,
251
  kRDFMask_bagID        = 1 << kRDFTerm_bagID
252
};
253
254
enum {
255
  kRDF_HasValueElem = 0x10000000UL  // ! Contains rdf:value child. Must fit within kXMP_ImplReservedMask!
256
};
257
258
// -------------------------------------------------------------------------------------------------
259
// GetRDFTermKind
260
// --------------
261
262
static RDFTermKind
263
GetRDFTermKind ( const XMP_VarString & name )
264
106k
{
265
106k
  RDFTermKind term = kRDFTerm_Other;
266
267
  // Arranged to hopefully minimize the parse time for large XMP.
268
269
106k
  if ( (name.size() > 4) && (strncmp ( name.c_str(), "rdf:", 4 ) == 0) ) {
270
271
25.0k
    if ( name == "rdf:li" ) {
272
17.1k
      term = kRDFTerm_li;
273
17.1k
    } else if ( name == "rdf:parseType" ) {
274
0
      term = kRDFTerm_parseType;
275
7.85k
    } else if ( name == "rdf:Description" ) {
276
558
      term = kRDFTerm_Description;
277
7.29k
    } else if ( name == "rdf:about" ) {
278
552
      term = kRDFTerm_about;
279
6.74k
    } else if ( name == "rdf:resource" ) {
280
0
      term = kRDFTerm_resource;
281
6.74k
    } else if ( name == "rdf:RDF" ) {
282
0
      term = kRDFTerm_RDF;
283
6.74k
    } else if ( name == "rdf:ID" ) {
284
0
      term = kRDFTerm_ID;
285
6.74k
    } else if ( name == "rdf:nodeID" ) {
286
0
      term = kRDFTerm_nodeID;
287
6.74k
    } else if ( name == "rdf:datatype" ) {
288
0
      term = kRDFTerm_datatype;
289
6.74k
    } else if ( name == "rdf:aboutEach" ) {
290
0
      term = kRDFTerm_aboutEach;
291
6.74k
    } else if ( name == "rdf:aboutEachPrefix" ) {
292
0
      term = kRDFTerm_aboutEachPrefix;
293
6.74k
    } else if ( name == "rdf:bagID" ) {
294
0
      term = kRDFTerm_bagID;
295
0
    }
296
297
25.0k
  }
298
299
106k
  return term;
300
301
106k
}  // GetRDFTermKind
302
303
304
// =================================================================================================
305
306
307
// -------------------------------------------------------------------------------------------------
308
// IsCoreSyntaxTerm
309
// ----------------
310
//
311
// 7.2.2 coreSyntaxTerms
312
//    rdf:RDF | rdf:ID | rdf:about | rdf:parseType | rdf:resource | rdf:nodeID | rdf:datatype
313
314
static bool
315
IsCoreSyntaxTerm ( RDFTermKind term )
316
24.7k
{
317
318
24.7k
  if  ( (kRDFTerm_FirstCore <= term) && (term <= kRDFTerm_LastCore) ) return true;
319
24.7k
  return false;
320
321
24.7k
}  // IsCoreSyntaxTerm
322
323
324
// -------------------------------------------------------------------------------------------------
325
// IsOldTerm
326
// ---------
327
//
328
// 7.2.4 oldTerms
329
//    rdf:aboutEach | rdf:aboutEachPrefix | rdf:bagID
330
331
static bool
332
IsOldTerm ( RDFTermKind term )
333
24.7k
{
334
335
24.7k
  if  ( (kRDFTerm_FirstOld <= term) && (term <= kRDFTerm_LastOld) ) return true;
336
24.7k
  return false;
337
338
24.7k
}  // IsOldTerm
339
340
// -------------------------------------------------------------------------------------------------
341
// IsPropertyElementName
342
// ---------------------
343
//
344
// 7.2.6 propertyElementURIs
345
//    anyURI - ( coreSyntaxTerms | rdf:Description | oldTerms )
346
347
static bool
348
IsPropertyElementName ( RDFTermKind term )
349
24.7k
{
350
351
24.7k
  if  ( (term == kRDFTerm_Description) || IsOldTerm ( term ) ) return false;
352
24.7k
  return (! IsCoreSyntaxTerm ( term ));
353
354
24.7k
}  // IsPropertyElementName
355
356
// =================================================================================================
357
// AddChildNode
358
// ============
359
360
static XMP_Node *
361
AddChildNode ( XMP_Node * xmpParent, const XML_Node & xmlNode, const XMP_StringPtr value, bool isTopLevel )
362
91.0k
{
363
  #if 0
364
    cout << "AddChildNode, parent = " << xmpParent->name << ", child = " << xmlNode.name;
365
    cout << ", value = \"" << value << '"';
366
    if ( isTopLevel ) cout << ", top level";
367
    cout << endl;
368
  #endif
369
  
370
91.0k
  if ( xmlNode.ns.empty() ) {
371
69
    XMP_Throw ( "XML namespace required for all elements and attributes", kXMPErr_BadRDF );
372
0
  }
373
    
374
90.9k
  XMP_StringPtr  childName    = xmlNode.name.c_str();
375
90.9k
  const bool     isArrayItem  = (xmlNode.name == "rdf:li");
376
90.9k
  const bool     isValueNode  = (xmlNode.name == "rdf:value");
377
90.9k
  XMP_OptionBits childOptions = 0;
378
  
379
90.9k
  if ( isTopLevel ) {
380
381
    // Lookup the schema node, adjust the XMP parent pointer.
382
66.1k
    XMP_Assert ( xmpParent->parent == 0 );  // Incoming parent must be the tree root.
383
66.1k
    XMP_Node * schemaNode = FindSchemaNode ( xmpParent, xmlNode.ns.c_str(), kXMP_CreateNodes );
384
66.1k
    if ( schemaNode->options & kXMP_NewImplicitNode ) schemaNode->options ^= kXMP_NewImplicitNode; // Clear the implicit node bit.
385
      // *** Should use "opt &= ~flag" (no conditional), need runtime check for proper 32 bit code.
386
66.1k
    xmpParent = schemaNode;
387
    
388
    // If this is an alias set the isAlias flag in the node and the hasAliases flag in the tree.
389
66.1k
    if ( sRegisteredAliasMap->find ( xmlNode.name ) != sRegisteredAliasMap->end() ) {
390
0
      childOptions |= kXMP_PropIsAlias;
391
0
      schemaNode->parent->options |= kXMP_PropHasAliases;
392
0
    }
393
    
394
66.1k
  }
395
396
  // Make sure that this is not a duplicate of a named node.
397
90.9k
  if ( ! (isArrayItem | isValueNode) ) {
398
73.8k
    if ( FindChildNode ( xmpParent, childName, kXMP_ExistingOnly ) != 0 ) {
399
14
      XMP_Throw ( "Duplicate property or field node", kXMPErr_BadXMP );
400
0
    }
401
    
402
73.8k
  }
403
  
404
  // Add the new child to the XMP parent node.
405
90.9k
  XMP_Node * newChild = new XMP_Node ( xmpParent, childName, value, childOptions );
406
90.9k
  if ( (! isValueNode) || xmpParent->children.empty() ) {
407
90.9k
     xmpParent->children.push_back ( newChild );
408
90.9k
  } else {
409
2
     xmpParent->children.insert ( xmpParent->children.begin(), newChild );
410
2
  }
411
90.9k
  if ( isValueNode ) {
412
0
    if ( isTopLevel || (! (xmpParent->options & kXMP_PropValueIsStruct)) ) XMP_Throw ( "Misplaced rdf:value element", kXMPErr_BadRDF );
413
0
    xmpParent->options |= kRDF_HasValueElem;
414
0
  }
415
  
416
90.9k
  if ( isArrayItem ) {
417
17.1k
    if ( ! (xmpParent->options & kXMP_PropValueIsArray) ) XMP_Throw ( "Misplaced rdf:li element", kXMPErr_BadRDF );
418
17.1k
    newChild->name = kXMP_ArrayItemName;
419
    #if 0 // *** XMP_DebugBuild
420
      newChild->_namePtr = newChild->name.c_str();
421
    #endif
422
17.1k
  }
423
  
424
90.9k
  return newChild;
425
426
90.9k
}  // AddChildNode
427
428
429
// =================================================================================================
430
// AddQualifierNode
431
// ================
432
433
static XMP_Node *
434
AddQualifierNode ( XMP_Node * xmpParent, const XMP_VarString & name, const XMP_VarString & value )
435
1.80k
{
436
437
  #if 0
438
    cout << "AddQualifierNode, parent = " << xmpParent->name << ", name = " << name;
439
    cout << ", value = \"" << value << '"' << endl;
440
  #endif
441
  
442
1.80k
  const bool isLang = (name == "xml:lang");
443
1.80k
  const bool isType = (name == "rdf:type");
444
445
1.80k
  XMP_Node * newQual = 0;
446
447
1.80k
    newQual = new XMP_Node ( xmpParent, name, value, kXMP_PropIsQualifier );
448
449
1.80k
    if ( ! (isLang | isType) ) {
450
0
      xmpParent->qualifiers.push_back ( newQual );
451
1.80k
    } else if ( isLang ) {
452
1.80k
      if ( xmpParent->qualifiers.empty() ) {
453
1.80k
        xmpParent->qualifiers.push_back ( newQual );
454
1.80k
      } else {
455
0
        xmpParent->qualifiers.insert ( xmpParent->qualifiers.begin(), newQual );
456
0
      }
457
1.80k
      xmpParent->options |= kXMP_PropHasLang;
458
1.80k
    } else {
459
0
      XMP_Assert ( isType );
460
0
      if ( xmpParent->qualifiers.empty() ) {
461
0
        xmpParent->qualifiers.push_back ( newQual );
462
0
      } else {
463
0
        size_t offset = 0;
464
0
        if ( XMP_PropHasLang ( xmpParent->options ) ) offset = 1;
465
0
        xmpParent->qualifiers.insert ( xmpParent->qualifiers.begin()+offset, newQual );
466
0
      }
467
0
      xmpParent->options |= kXMP_PropHasType;
468
0
    }
469
470
1.80k
    xmpParent->options |= kXMP_PropHasQualifiers;
471
472
1.80k
  return newQual;
473
474
1.80k
}  // AddQualifierNode
475
476
477
// =================================================================================================
478
// AddQualifierNode
479
// ================
480
481
static XMP_Node *
482
AddQualifierNode ( XMP_Node * xmpParent, const XML_Node & attr )
483
1.80k
{
484
1.80k
  if ( attr.ns.empty() ) {
485
0
    XMP_Throw ( "XML namespace required for all elements and attributes", kXMPErr_BadRDF );
486
0
  }
487
  
488
1.80k
  return AddQualifierNode ( xmpParent, attr.name, attr.value );
489
490
1.80k
}  // AddQualifierNode
491
492
493
// =================================================================================================
494
// FixupQualifiedNode
495
// ==================
496
//
497
// The parent is an RDF pseudo-struct containing an rdf:value field. Fix the XMP data model. The
498
// rdf:value node must be the first child, the other children are qualifiers. The form, value, and
499
// children of the rdf:value node are the real ones. The rdf:value node's qualifiers must be added
500
// to the others.
501
  
502
static void
503
FixupQualifiedNode ( XMP_Node * xmpParent )
504
0
{
505
0
  size_t qualNum, qualLim;
506
0
  size_t childNum, childLim;
507
508
0
  XMP_Enforce ( (xmpParent->options & kXMP_PropValueIsStruct) && (! xmpParent->children.empty()) );
509
510
0
  XMP_Node * valueNode = xmpParent->children[0];
511
0
  XMP_Enforce ( valueNode->name == "rdf:value" );
512
  
513
0
  xmpParent->qualifiers.reserve ( xmpParent->qualifiers.size() + xmpParent->children.size() + valueNode->qualifiers.size() );
514
  
515
  // Move the qualifiers on the value node to the parent. Make sure an xml:lang qualifier stays at
516
  // the front. Check for duplicate names between the value node's qualifiers and the parent's
517
  // children. The parent's children are about to become qualifiers. Check here, between the
518
  // groups. Intra-group duplicates are caught by AddChildNode.
519
  
520
0
  qualNum = 0;
521
0
  qualLim = valueNode->qualifiers.size();
522
  
523
0
  if ( valueNode->options & kXMP_PropHasLang ) {
524
525
0
    if ( xmpParent->options & kXMP_PropHasLang ) XMP_Throw ( "Redundant xml:lang for rdf:value element", kXMPErr_BadXMP );
526
527
0
    XMP_Node * langQual = valueNode->qualifiers[0];
528
    
529
0
    XMP_Assert ( langQual->name == "xml:lang" );
530
0
    langQual->parent = xmpParent;
531
0
    xmpParent->options |= kXMP_PropHasLang;
532
533
0
    if ( xmpParent->qualifiers.empty() ) {
534
0
      xmpParent->qualifiers.push_back ( langQual ); // *** Should use utilities to add qual & set parent.
535
0
    } else {
536
0
      xmpParent->qualifiers.insert ( xmpParent->qualifiers.begin(), langQual );
537
0
    }
538
0
    valueNode->qualifiers[0] = 0; // We just moved it to the parent.
539
540
0
    qualNum = 1;  // Start the remaining copy after the xml:lang qualifier.
541
542
0
  }
543
  
544
0
  for ( ; qualNum != qualLim; ++qualNum ) {
545
546
0
    XMP_Node * currQual = valueNode->qualifiers[qualNum];
547
0
    if ( FindChildNode ( xmpParent, currQual->name.c_str(), kXMP_ExistingOnly ) != 0 ) {
548
0
      XMP_Throw ( "Duplicate qualifier node", kXMPErr_BadXMP );
549
0
    }
550
551
0
    currQual->parent = xmpParent;
552
0
    xmpParent->qualifiers.push_back ( currQual );
553
0
    valueNode->qualifiers[qualNum] = 0; // We just moved it to the parent.
554
555
0
  }
556
  
557
0
  valueNode->qualifiers.clear();  // ! There should be nothing but null pointers.
558
  
559
  // Change the parent's other children into qualifiers. This loop starts at 1, child 0 is the
560
  // rdf:value node. Put xml:lang at the front, append all others.
561
  
562
0
  for ( childNum = 1, childLim = xmpParent->children.size(); childNum != childLim; ++childNum ) {
563
564
0
    XMP_Node * currQual = xmpParent->children[childNum];
565
  
566
0
      bool isLang = (currQual->name == "xml:lang");
567
      
568
0
      currQual->options |= kXMP_PropIsQualifier;
569
0
      currQual->parent = xmpParent;
570
571
0
      if ( isLang ) {
572
0
        if ( xmpParent->options & kXMP_PropHasLang ) XMP_Throw ( "Duplicate xml:lang qualifier", kXMPErr_BadXMP );
573
0
        xmpParent->options |= kXMP_PropHasLang;
574
0
      } else if ( currQual->name == "rdf:type" ) {
575
0
        xmpParent->options |= kXMP_PropHasType;
576
0
      }
577
578
0
      if ( (! isLang) || xmpParent->qualifiers.empty() ) {
579
0
        xmpParent->qualifiers.push_back ( currQual );
580
0
      } else {
581
0
        xmpParent->qualifiers.insert ( xmpParent->qualifiers.begin(), currQual );
582
0
      }
583
0
      xmpParent->children[childNum] = 0;  // We just moved it to the qualifers.
584
    
585
0
  }
586
  
587
0
  if ( ! xmpParent->qualifiers.empty() ) xmpParent->options |= kXMP_PropHasQualifiers;
588
  
589
  // Move the options and value last, other checks need the parent's original options. Move the
590
  // value node's children to be the parent's children. Delete the now useless value node.
591
  
592
0
  XMP_Assert ( xmpParent->options & (kXMP_PropValueIsStruct | kRDF_HasValueElem) );
593
0
  xmpParent->options &= ~ (static_cast<unsigned long>(kXMP_PropValueIsStruct) | static_cast<unsigned long>(kRDF_HasValueElem));
594
0
  xmpParent->options |= valueNode->options;
595
  
596
0
  xmpParent->value.swap ( valueNode->value );
597
  #if 0 // *** XMP_DebugBuild
598
    xmpParent->_valuePtr = xmpParent->value.c_str();
599
  #endif
600
601
0
  xmpParent->children[0] = 0; // ! Remove the value node itself before the swap.
602
0
  xmpParent->children.swap ( valueNode->children );
603
  
604
0
  for ( size_t childNum = 0, childLim = xmpParent->children.size(); childNum != childLim; ++childNum ) {
605
0
    XMP_Node * currChild = xmpParent->children[childNum];
606
0
    currChild->parent = xmpParent;
607
0
  }
608
609
0
  delete valueNode;
610
  
611
0
}  // FixupQualifiedNode
612
613
614
// =================================================================================================
615
// ProcessRDF
616
// ==========
617
//
618
// Parse the XML tree of the RDF and build the corresponding XMP tree.
619
620
// *** Throw an exception if no XMP is found? By option?
621
// *** Do parsing exceptions cause the partial tree to be deleted?
622
623
void ProcessRDF ( XMP_Node * xmpTree, const XML_Node & rdfNode, XMP_OptionBits options )
624
578
{
625
578
  IgnoreParam(options);
626
  
627
578
  RDF_RDF ( xmpTree, rdfNode );
628
629
578
}  // ProcessRDF
630
631
632
// =================================================================================================
633
// RDF_RDF
634
// =======
635
//
636
// 7.2.9 RDF
637
//    start-element ( URI == rdf:RDF, attributes == set() )
638
//    nodeElementList
639
//    end-element()
640
//
641
// The top level rdf:RDF node. It can only have xmlns attributes, which have already been removed
642
// during construction of the XML tree.
643
644
static void
645
RDF_RDF ( XMP_Node * xmpTree, const XML_Node & xmlNode )
646
578
{
647
648
578
  if ( ! xmlNode.attrs.empty() ) XMP_Throw ( "Invalid attributes of rdf:RDF element", kXMPErr_BadRDF );
649
578
  RDF_NodeElementList ( xmpTree, xmlNode, kIsTopLevel );
650
651
578
}  // RDF_RDF
652
653
654
// =================================================================================================
655
// RDF_NodeElementList
656
// ===================
657
//
658
// 7.2.10 nodeElementList
659
//    ws* ( nodeElement ws* )*
660
661
static void
662
RDF_NodeElementList ( XMP_Node * xmpParent, const XML_Node & xmlParent, bool isTopLevel )
663
578
{
664
578
  XMP_Assert ( isTopLevel );
665
  
666
578
  XML_cNodePos currChild = xmlParent.content.begin(); // *** Change these loops to the indexed pattern.
667
578
  XML_cNodePos endChild  = xmlParent.content.end();
668
669
2.98k
  for ( ; currChild != endChild; ++currChild ) {
670
2.40k
    if ( (*currChild)->IsWhitespaceNode() ) continue;
671
578
    RDF_NodeElement ( xmpParent, **currChild, isTopLevel );
672
578
  }
673
674
578
}  // RDF_NodeElementList
675
676
677
// =================================================================================================
678
// RDF_NodeElement
679
// ===============
680
//
681
// 7.2.5 nodeElementURIs
682
//    anyURI - ( coreSyntaxTerms | rdf:li | oldTerms )
683
//
684
// 7.2.11 nodeElement
685
//    start-element ( URI == nodeElementURIs,
686
//            attributes == set ( ( idAttr | nodeIdAttr | aboutAttr )?, propertyAttr* ) )
687
//    propertyEltList
688
//    end-element()
689
//
690
// A node element URI is rdf:Description or anything else that is not an RDF term.
691
692
static void
693
RDF_NodeElement ( XMP_Node * xmpParent, const XML_Node & xmlNode, bool isTopLevel )
694
7.31k
{
695
7.31k
  RDFTermKind nodeTerm = GetRDFTermKind ( xmlNode.name );
696
7.31k
  if ( (nodeTerm != kRDFTerm_Description) && (nodeTerm != kRDFTerm_Other) ) {
697
0
    XMP_Throw ( "Node element must be rdf:Description or typedNode", kXMPErr_BadRDF );
698
0
  }
699
700
7.31k
  if ( isTopLevel && (nodeTerm == kRDFTerm_Other) ) {
701
20
    XMP_Throw ( "Top level typedNode not allowed", kXMPErr_BadXMP );
702
7.29k
  } else {
703
7.29k
    RDF_NodeElementAttrs ( xmpParent, xmlNode, isTopLevel );
704
7.29k
    RDF_PropertyElementList ( xmpParent, xmlNode, isTopLevel );
705
7.29k
  }
706
707
7.31k
}  // RDF_NodeElement
708
709
710
// =================================================================================================
711
// RDF_NodeElementAttrs
712
// ====================
713
//
714
// 7.2.7 propertyAttributeURIs
715
//    anyURI - ( coreSyntaxTerms | rdf:Description | rdf:li | oldTerms )
716
//
717
// 7.2.11 nodeElement
718
//    start-element ( URI == nodeElementURIs,
719
//            attributes == set ( ( idAttr | nodeIdAttr | aboutAttr )?, propertyAttr* ) )
720
//    propertyEltList
721
//    end-element()
722
//
723
// Process the attribute list for an RDF node element. A property attribute URI is anything other
724
// than an RDF term. The rdf:ID and rdf:nodeID attributes are simply ignored, as are rdf:about
725
// attributes on inner nodes.
726
727
static const XMP_OptionBits kExclusiveAttrMask = (kRDFMask_ID | kRDFMask_nodeID | kRDFMask_about);
728
729
static void
730
RDF_NodeElementAttrs ( XMP_Node * xmpParent, const XML_Node & xmlNode, bool isTopLevel )
731
7.29k
{
732
7.29k
  XMP_OptionBits exclusiveAttrs = 0;  // Used to detect attributes that are mutually exclusive.
733
734
7.29k
  XML_cNodePos currAttr = xmlNode.attrs.begin();
735
7.29k
  XML_cNodePos endAttr  = xmlNode.attrs.end();
736
737
66.3k
  for ( ; currAttr != endAttr; ++currAttr ) {
738
739
59.1k
    RDFTermKind attrTerm = GetRDFTermKind ( (*currAttr)->name );
740
741
59.1k
    switch ( attrTerm ) {
742
743
0
      case kRDFTerm_ID     :
744
0
      case kRDFTerm_nodeID :
745
552
      case kRDFTerm_about  :
746
747
552
        if ( exclusiveAttrs & kExclusiveAttrMask ) XMP_Throw ( "Mutally exclusive about, ID, nodeID attributes", kXMPErr_BadRDF );
748
552
        exclusiveAttrs |= (1 << attrTerm);
749
750
552
        if ( isTopLevel && (attrTerm == kRDFTerm_about) ) {
751
          // This is the rdf:about attribute on a top level node. Set the XMP tree name if
752
          // it doesn't have a name yet. Make sure this name matches the XMP tree name.
753
552
          XMP_Assert ( xmpParent->parent == 0 );  // Must be the tree root node.
754
552
          if ( xmpParent->name.empty() ) {
755
552
            xmpParent->name = (*currAttr)->value;
756
552
          } else if ( ! (*currAttr)->value.empty() ) {
757
0
            if ( xmpParent->name != (*currAttr)->value ) XMP_Throw ( "Mismatched top level rdf:about values", kXMPErr_BadXMP );
758
0
          }
759
552
        }
760
761
552
        break;
762
763
58.5k
      case kRDFTerm_Other :
764
58.5k
        AddChildNode ( xmpParent, **currAttr, (*currAttr)->value.c_str(), isTopLevel );
765
58.5k
        break;
766
767
0
      default :
768
0
        XMP_Throw ( "Invalid nodeElement attribute", kXMPErr_BadRDF );
769
770
59.1k
    }
771
772
59.1k
  }
773
774
7.29k
}  // RDF_NodeElementAttrs
775
776
777
// =================================================================================================
778
// RDF_PropertyElementList
779
// =======================
780
//
781
// 7.2.13 propertyEltList
782
//    ws* ( propertyElt ws* )*
783
784
static void
785
RDF_PropertyElementList ( XMP_Node * xmpParent, const XML_Node & xmlParent, bool isTopLevel )
786
7.23k
{
787
7.23k
  XML_cNodePos currChild = xmlParent.content.begin();
788
7.23k
  XML_cNodePos endChild  = xmlParent.content.end();
789
790
95.5k
  for ( ; currChild != endChild; ++currChild ) {
791
88.4k
    if ( (*currChild)->IsWhitespaceNode() ) continue;
792
24.8k
    if ( (*currChild)->kind != kElemNode ) {
793
81
      XMP_Throw ( "Expected property element node not found", kXMPErr_BadRDF );
794
0
    }
795
24.7k
    RDF_PropertyElement ( xmpParent, **currChild, isTopLevel );
796
24.7k
  }
797
798
7.23k
}  // RDF_PropertyElementList
799
800
801
// =================================================================================================
802
// RDF_PropertyElement
803
// ===================
804
//
805
// 7.2.14 propertyElt
806
//    resourcePropertyElt | literalPropertyElt | parseTypeLiteralPropertyElt |
807
//    parseTypeResourcePropertyElt | parseTypeCollectionPropertyElt | parseTypeOtherPropertyElt | emptyPropertyElt
808
//
809
// 7.2.15 resourcePropertyElt
810
//    start-element ( URI == propertyElementURIs, attributes == set ( idAttr? ) )
811
//    ws* nodeElement ws*
812
//    end-element()
813
//
814
// 7.2.16 literalPropertyElt
815
//    start-element ( URI == propertyElementURIs, attributes == set ( idAttr?, datatypeAttr?) )
816
//    text()
817
//    end-element()
818
//
819
// 7.2.17 parseTypeLiteralPropertyElt
820
//    start-element ( URI == propertyElementURIs, attributes == set ( idAttr?, parseLiteral ) )
821
//    literal
822
//    end-element()
823
//
824
// 7.2.18 parseTypeResourcePropertyElt
825
//    start-element ( URI == propertyElementURIs, attributes == set ( idAttr?, parseResource ) )
826
//    propertyEltList
827
//    end-element()
828
//
829
// 7.2.19 parseTypeCollectionPropertyElt
830
//    start-element ( URI == propertyElementURIs, attributes == set ( idAttr?, parseCollection ) )
831
//    nodeElementList
832
//    end-element()
833
//
834
// 7.2.20 parseTypeOtherPropertyElt
835
//    start-element ( URI == propertyElementURIs, attributes == set ( idAttr?, parseOther ) )
836
//    propertyEltList
837
//    end-element()
838
//
839
// 7.2.21 emptyPropertyElt
840
//    start-element ( URI == propertyElementURIs,
841
//            attributes == set ( idAttr?, ( resourceAttr | nodeIdAttr )?, propertyAttr* ) )
842
//    end-element()
843
//
844
// The various property element forms are not distinguished by the XML element name, but by their
845
// attributes for the most part. The exceptions are resourcePropertyElt and literalPropertyElt. They
846
// are distinguished by their XML element content.
847
//
848
// NOTE: The RDF syntax does not explicitly include the xml:lang attribute although it can appear in
849
// many of these. We have to allow for it in the attribute counts below.
850
851
static void
852
RDF_PropertyElement ( XMP_Node * xmpParent, const XML_Node & xmlNode, bool isTopLevel )
853
24.7k
{
854
24.7k
  RDFTermKind nodeTerm = GetRDFTermKind ( xmlNode.name );
855
24.7k
  if ( ! IsPropertyElementName ( nodeTerm ) ) XMP_Throw ( "Invalid property element name", kXMPErr_BadRDF );
856
  
857
24.7k
  if ( xmlNode.attrs.size() > 3 ) {
858
859
    // Only an emptyPropertyElt can have more than 3 attributes.
860
854
    RDF_EmptyPropertyElement ( xmpParent, xmlNode, isTopLevel );
861
862
23.8k
  } else {
863
864
    // Look through the attributes for one that isn't rdf:ID or xml:lang, it will usually tell
865
    // what we should be dealing with. The called routines must verify their specific syntax!
866
867
23.8k
    XML_cNodePos currAttr = xmlNode.attrs.begin();
868
23.8k
    XML_cNodePos endAttr  = xmlNode.attrs.end();
869
23.8k
    XMP_VarString * attrName = 0;
870
871
25.7k
    for ( ; currAttr != endAttr; ++currAttr ) {
872
3.51k
      attrName = &((*currAttr)->name);
873
3.51k
      if ( (*attrName != "xml:lang") && (*attrName != "rdf:ID") ) break;
874
3.51k
    }
875
876
23.8k
    if ( currAttr != endAttr ) {
877
878
1.71k
      XMP_Assert ( attrName != 0 );
879
1.71k
      XMP_VarString& attrValue = (*currAttr)->value;
880
881
1.71k
      if ( *attrName == "rdf:datatype" ) {
882
0
        RDF_LiteralPropertyElement ( xmpParent, xmlNode, isTopLevel );
883
1.71k
      } else if ( *attrName != "rdf:parseType" ) {
884
1.71k
        RDF_EmptyPropertyElement ( xmpParent, xmlNode, isTopLevel );
885
1.71k
      } else if ( attrValue == "Literal" ) {
886
0
        RDF_ParseTypeLiteralPropertyElement ( xmpParent, xmlNode, isTopLevel );
887
0
      } else if ( attrValue == "Resource" ) {
888
0
        RDF_ParseTypeResourcePropertyElement ( xmpParent, xmlNode, isTopLevel );
889
0
      } else if ( attrValue == "Collection" ) {
890
0
        RDF_ParseTypeCollectionPropertyElement ( xmpParent, xmlNode, isTopLevel );
891
0
      } else {
892
0
        RDF_ParseTypeOtherPropertyElement ( xmpParent, xmlNode, isTopLevel );
893
0
      }
894
895
22.1k
    } else {
896
897
      // Only rdf:ID and xml:lang, could be a resourcePropertyElt, a literalPropertyElt, or an.
898
      // emptyPropertyElt. Look at the child XML nodes to decide which.
899
900
22.1k
      if ( xmlNode.content.empty() ) {
901
902
0
        RDF_EmptyPropertyElement ( xmpParent, xmlNode, isTopLevel );
903
904
22.1k
      } else {
905
      
906
22.1k
        XML_cNodePos currChild = xmlNode.content.begin();
907
22.1k
        XML_cNodePos endChild  = xmlNode.content.end();
908
909
51.4k
        for ( ; currChild != endChild; ++currChild ) {
910
36.0k
          if ( (*currChild)->kind != kCDataNode ) break;
911
36.0k
        }
912
        
913
22.1k
        if ( currChild == endChild ) {
914
15.4k
          RDF_LiteralPropertyElement ( xmpParent, xmlNode, isTopLevel );
915
15.4k
        } else {
916
6.76k
          RDF_ResourcePropertyElement ( xmpParent, xmlNode, isTopLevel );
917
6.76k
        }
918
      
919
22.1k
      }
920
921
22.1k
    }
922
    
923
23.8k
  }
924
925
24.7k
}  // RDF_PropertyElement
926
927
928
// =================================================================================================
929
// RDF_ResourcePropertyElement
930
// ===========================
931
//
932
// 7.2.15 resourcePropertyElt
933
//    start-element ( URI == propertyElementURIs, attributes == set ( idAttr? ) )
934
//    ws* nodeElement ws*
935
//    end-element()
936
//
937
// This handles structs using an rdf:Description node, arrays using rdf:Bag/Seq/Alt, and typedNodes.
938
// It also catches and cleans up qualified properties written with rdf:Description and rdf:value.
939
940
static void
941
RDF_ResourcePropertyElement ( XMP_Node * xmpParent, const XML_Node & xmlNode, bool isTopLevel )
942
6.76k
{
943
6.76k
  if ( isTopLevel && (xmlNode.name == "iX:changes") ) return; // Strip old "punchcard" chaff.
944
  
945
6.76k
  XMP_Node * newCompound = AddChildNode ( xmpParent, xmlNode, "", isTopLevel );
946
  
947
6.76k
  XML_cNodePos currAttr = xmlNode.attrs.begin();
948
6.76k
  XML_cNodePos endAttr  = xmlNode.attrs.end();
949
950
6.76k
  for ( ; currAttr != endAttr; ++currAttr ) {
951
0
    XMP_VarString & attrName = (*currAttr)->name;
952
0
    if ( attrName == "xml:lang" ) {
953
0
      AddQualifierNode ( newCompound, **currAttr );
954
0
    } else if ( attrName == "rdf:ID" ) {
955
0
      continue; // Ignore all rdf:ID attributes.
956
0
    } else {
957
0
      XMP_Throw ( "Invalid attribute for resource property element", kXMPErr_BadRDF );
958
0
    }
959
0
  }
960
  
961
6.76k
  XML_cNodePos currChild = xmlNode.content.begin();
962
6.76k
  XML_cNodePos endChild  = xmlNode.content.end();
963
964
20.2k
  for ( ; currChild != endChild; ++currChild ) {
965
20.2k
    if ( ! (*currChild)->IsWhitespaceNode() ) break;
966
20.2k
  }
967
6.76k
  if ( currChild == endChild ) XMP_Throw ( "Missing child of resource property element", kXMPErr_BadRDF );
968
6.76k
  if ( (*currChild)->kind != kElemNode ) XMP_Throw ( "Children of resource property element must be XML elements", kXMPErr_BadRDF );
969
970
6.75k
  if ( (*currChild)->name == "rdf:Bag" ) {
971
879
    newCompound->options |= kXMP_PropValueIsArray;
972
5.87k
  } else if ( (*currChild)->name == "rdf:Seq" ) {
973
4.04k
    newCompound->options |= kXMP_PropValueIsArray | kXMP_PropArrayIsOrdered;
974
4.04k
  } else if ( (*currChild)->name == "rdf:Alt" ) {
975
1.80k
    newCompound->options |= kXMP_PropValueIsArray | kXMP_PropArrayIsOrdered | kXMP_PropArrayIsAlternate;
976
1.80k
  } else {
977
14
    newCompound->options |= kXMP_PropValueIsStruct;
978
14
    if ( (*currChild)->name != "rdf:Description" ) {
979
0
      XMP_VarString typeName ( (*currChild)->ns );
980
0
      size_t        colonPos = (*currChild)->name.find_first_of(':');
981
0
      if ( colonPos == XMP_VarString::npos ) XMP_Throw ( "All XML elements must be in a namespace", kXMPErr_BadXMP );
982
0
      typeName.append ( (*currChild)->name, colonPos, XMP_VarString::npos );
983
0
      AddQualifierNode ( newCompound, XMP_VarString("rdf:type"), typeName );
984
0
    }
985
14
  }
986
987
6.75k
  RDF_NodeElement ( newCompound, **currChild, kNotTopLevel );
988
6.75k
  if ( newCompound->options & kRDF_HasValueElem ) {
989
0
    FixupQualifiedNode ( newCompound );
990
6.75k
  } else if ( newCompound->options & kXMP_PropArrayIsAlternate ) {
991
1.80k
    DetectAltText ( newCompound );
992
1.80k
  }
993
994
20.0k
  for ( ++currChild; currChild != endChild; ++currChild ) {
995
13.2k
    if ( ! (*currChild)->IsWhitespaceNode() ) XMP_Throw ( "Invalid child of resource property element", kXMPErr_BadRDF );
996
13.2k
  }
997
998
6.75k
}  // RDF_ResourcePropertyElement
999
1000
1001
// =================================================================================================
1002
// RDF_LiteralPropertyElement
1003
// ==========================
1004
//
1005
// 7.2.16 literalPropertyElt
1006
//    start-element ( URI == propertyElementURIs, attributes == set ( idAttr?, datatypeAttr?) )
1007
//    text()
1008
//    end-element()
1009
//
1010
// Add a leaf node with the text value and qualifiers for the attributes.
1011
1012
static void
1013
RDF_LiteralPropertyElement ( XMP_Node * xmpParent, const XML_Node & xmlNode, bool isTopLevel )
1014
15.4k
{
1015
15.4k
  XMP_Node * newChild = AddChildNode ( xmpParent, xmlNode, "", isTopLevel );
1016
  
1017
15.4k
  XML_cNodePos currAttr = xmlNode.attrs.begin();
1018
15.4k
  XML_cNodePos endAttr  = xmlNode.attrs.end();
1019
1020
17.2k
  for ( ; currAttr != endAttr; ++currAttr ) {
1021
1.80k
    XMP_VarString & attrName = (*currAttr)->name;
1022
1.80k
    if ( attrName == "xml:lang" ) {
1023
1.80k
      AddQualifierNode ( newChild, **currAttr );
1024
1.80k
    } else if ( (attrName == "rdf:ID") || (attrName == "rdf:datatype") ) {
1025
0
      continue; // Ignore all rdf:ID and rdf:datatype attributes.
1026
0
    } else {
1027
0
      XMP_Throw ( "Invalid attribute for literal property element", kXMPErr_BadRDF );
1028
0
    }
1029
1.80k
  }
1030
  
1031
15.4k
  XML_cNodePos currChild = xmlNode.content.begin();
1032
15.4k
  XML_cNodePos endChild  = xmlNode.content.end();
1033
15.4k
  size_t      textSize  = 0;
1034
1035
31.1k
  for ( ; currChild != endChild; ++currChild ) {
1036
15.7k
    if ( (*currChild)->kind != kCDataNode ) XMP_Throw ( "Invalid child of literal property element", kXMPErr_BadRDF );
1037
15.7k
    textSize += (*currChild)->value.size();
1038
15.7k
  }
1039
  
1040
15.4k
  newChild->value.reserve ( textSize );
1041
1042
31.1k
  for ( currChild = xmlNode.content.begin(); currChild != endChild; ++currChild ) {
1043
15.7k
    newChild->value += (*currChild)->value;
1044
15.7k
  }
1045
1046
  #if 0 // *** XMP_DebugBuild
1047
    newChild->_valuePtr = newChild->value.c_str();
1048
  #endif
1049
  
1050
15.4k
}  // RDF_LiteralPropertyElement
1051
1052
1053
// =================================================================================================
1054
// RDF_ParseTypeLiteralPropertyElement
1055
// ===================================
1056
//
1057
// 7.2.17 parseTypeLiteralPropertyElt
1058
//    start-element ( URI == propertyElementURIs, attributes == set ( idAttr?, parseLiteral ) )
1059
//    literal
1060
//    end-element()
1061
1062
static void
1063
RDF_ParseTypeLiteralPropertyElement ( XMP_Node * xmpParent, const XML_Node & xmlNode, bool isTopLevel )
1064
0
{
1065
0
  IgnoreParam(xmpParent); IgnoreParam(xmlNode); IgnoreParam(isTopLevel); 
1066
  
1067
0
  XMP_Throw ( "ParseTypeLiteral property element not allowed", kXMPErr_BadXMP );
1068
1069
0
}  // RDF_ParseTypeLiteralPropertyElement
1070
1071
1072
// =================================================================================================
1073
// RDF_ParseTypeResourcePropertyElement
1074
// ====================================
1075
//
1076
// 7.2.18 parseTypeResourcePropertyElt
1077
//    start-element ( URI == propertyElementURIs, attributes == set ( idAttr?, parseResource ) )
1078
//    propertyEltList
1079
//    end-element()
1080
//
1081
// Add a new struct node with a qualifier for the possible rdf:ID attribute. Then process the XML
1082
// child nodes to get the struct fields.
1083
1084
static void
1085
RDF_ParseTypeResourcePropertyElement ( XMP_Node * xmpParent, const XML_Node & xmlNode, bool isTopLevel )
1086
0
{
1087
1088
0
  XMP_Node * newStruct = AddChildNode ( xmpParent, xmlNode, "", isTopLevel );
1089
0
  newStruct->options  |= kXMP_PropValueIsStruct;
1090
  
1091
0
  XML_cNodePos currAttr = xmlNode.attrs.begin();
1092
0
  XML_cNodePos endAttr  = xmlNode.attrs.end();
1093
1094
0
  for ( ; currAttr != endAttr; ++currAttr ) {
1095
0
    XMP_VarString & attrName = (*currAttr)->name;
1096
0
    if ( attrName == "rdf:parseType" ) {
1097
0
      continue; // ! The caller ensured the value is "Resource".
1098
0
    } else if ( attrName == "xml:lang" ) {
1099
0
      AddQualifierNode ( newStruct, **currAttr );
1100
0
    } else if ( attrName == "rdf:ID" ) {
1101
0
      continue; // Ignore all rdf:ID attributes.
1102
0
    } else {
1103
0
      XMP_Throw ( "Invalid attribute for ParseTypeResource property element", kXMPErr_BadRDF );
1104
0
    }
1105
0
  }
1106
1107
0
  RDF_PropertyElementList ( newStruct, xmlNode, kNotTopLevel );
1108
1109
0
  if ( newStruct->options & kRDF_HasValueElem ) FixupQualifiedNode ( newStruct );
1110
  
1111
  // *** Need to look for arrays using rdf:Description and rdf:type.
1112
1113
0
}  // RDF_ParseTypeResourcePropertyElement
1114
1115
1116
// =================================================================================================
1117
// RDF_ParseTypeCollectionPropertyElement
1118
// ======================================
1119
//
1120
// 7.2.19 parseTypeCollectionPropertyElt
1121
//    start-element ( URI == propertyElementURIs, attributes == set ( idAttr?, parseCollection ) )
1122
//    nodeElementList
1123
//    end-element()
1124
1125
static void
1126
RDF_ParseTypeCollectionPropertyElement ( XMP_Node * xmpParent, const XML_Node & xmlNode, bool isTopLevel )
1127
0
{
1128
0
  IgnoreParam(xmpParent); IgnoreParam(xmlNode); IgnoreParam(isTopLevel); 
1129
1130
0
  XMP_Throw ( "ParseTypeCollection property element not allowed", kXMPErr_BadXMP );
1131
1132
0
}  // RDF_ParseTypeCollectionPropertyElement
1133
1134
1135
// =================================================================================================
1136
// RDF_ParseTypeOtherPropertyElement
1137
// =================================
1138
//
1139
// 7.2.20 parseTypeOtherPropertyElt
1140
//    start-element ( URI == propertyElementURIs, attributes == set ( idAttr?, parseOther ) )
1141
//    propertyEltList
1142
//    end-element()
1143
1144
static void
1145
RDF_ParseTypeOtherPropertyElement ( XMP_Node * xmpParent, const XML_Node & xmlNode, bool isTopLevel )
1146
0
{
1147
0
  IgnoreParam(xmpParent); IgnoreParam(xmlNode); IgnoreParam(isTopLevel); 
1148
1149
0
  XMP_Throw ( "ParseTypeOther property element not allowed", kXMPErr_BadXMP );
1150
1151
0
}  // RDF_ParseTypeOtherPropertyElement
1152
1153
1154
// =================================================================================================
1155
// RDF_EmptyPropertyElement
1156
// ========================
1157
//
1158
// 7.2.21 emptyPropertyElt
1159
//    start-element ( URI == propertyElementURIs,
1160
//            attributes == set ( idAttr?, ( resourceAttr | nodeIdAttr )?, propertyAttr* ) )
1161
//    end-element()
1162
//
1163
//  <ns:Prop1/>  <!-- a simple property with an empty value --> 
1164
//  <ns:Prop2 rdf:resource="http://www.adobe.com/"/> <!-- a URI value --> 
1165
//  <ns:Prop3 rdf:value="..." ns:Qual="..."/> <!-- a simple qualified property --> 
1166
//  <ns:Prop4 ns:Field1="..." ns:Field2="..."/> <!-- a struct with simple fields -->
1167
//
1168
// An emptyPropertyElt is an element with no contained content, just a possibly empty set of
1169
// attributes. An emptyPropertyElt can represent three special cases of simple XMP properties: a
1170
// simple property with an empty value (ns:Prop1), a simple property whose value is a URI
1171
// (ns:Prop2), or a simple property with simple qualifiers (ns:Prop3). An emptyPropertyElt can also
1172
// represent an XMP struct whose fields are all simple and unqualified (ns:Prop4).
1173
//
1174
// It is an error to use both rdf:value and rdf:resource - that can lead to invalid  RDF in the
1175
// verbose form written using a literalPropertyElt.
1176
//
1177
// The XMP mapping for an emptyPropertyElt is a bit different from generic RDF, partly for 
1178
// design reasons and partly for historical reasons. The XMP mapping rules are: 
1179
//  1. If there is an rdf:value attribute then this is a simple property with a text value.
1180
//    All other attributes are qualifiers.
1181
//  2. If there is an rdf:resource attribute then this is a simple property with a URI value. 
1182
//    All other attributes are qualifiers.
1183
//  3. If there are no attributes other than xml:lang, rdf:ID, or rdf:nodeID then this is a simple 
1184
//    property with an empty value. 
1185
//  4. Otherwise this is a struct, the attributes other than xml:lang, rdf:ID, or rdf:nodeID are fields. 
1186
1187
static void
1188
RDF_EmptyPropertyElement ( XMP_Node * xmpParent, const XML_Node & xmlNode, bool isTopLevel )
1189
2.56k
{
1190
2.56k
  bool hasPropertyAttrs = false;
1191
2.56k
  bool hasResourceAttr  = false;
1192
2.56k
  bool hasNodeIDAttr    = false;
1193
2.56k
  bool hasValueAttr     = false;
1194
  
1195
2.56k
  const XML_Node * valueNode = 0; // ! Can come from rdf:value or rdf:resource.
1196
  
1197
2.56k
  if ( ! xmlNode.content.empty() ) XMP_Throw ( "Nested content not allowed with rdf:resource or property attributes", kXMPErr_BadRDF );
1198
  
1199
  // First figure out what XMP this maps to and remember the XML node for a simple value.
1200
  
1201
2.56k
  XML_cNodePos currAttr = xmlNode.attrs.begin();
1202
2.56k
  XML_cNodePos endAttr  = xmlNode.attrs.end();
1203
1204
10.2k
  for ( ; currAttr != endAttr; ++currAttr ) {
1205
1206
7.68k
    RDFTermKind attrTerm = GetRDFTermKind ( (*currAttr)->name );
1207
1208
7.68k
    switch ( attrTerm ) {
1209
1210
0
      case kRDFTerm_ID :
1211
        // Nothing to do.
1212
0
        break;
1213
1214
0
      case kRDFTerm_resource :
1215
0
        if ( hasNodeIDAttr ) XMP_Throw ( "Empty property element can't have both rdf:resource and rdf:nodeID", kXMPErr_BadRDF );
1216
0
        if ( hasValueAttr ) XMP_Throw ( "Empty property element can't have both rdf:value and rdf:resource", kXMPErr_BadXMP );
1217
0
        hasResourceAttr = true;
1218
0
        if ( ! hasValueAttr ) valueNode = *currAttr;
1219
0
        break;
1220
1221
0
      case kRDFTerm_nodeID :
1222
0
        if ( hasResourceAttr ) XMP_Throw ( "Empty property element can't have both rdf:resource and rdf:nodeID", kXMPErr_BadRDF );
1223
0
        hasNodeIDAttr = true;
1224
0
        break;
1225
1226
7.68k
      case kRDFTerm_Other :
1227
7.68k
        if ( (*currAttr)->name == "rdf:value" ) {
1228
0
          if ( hasResourceAttr ) XMP_Throw ( "Empty property element can't have both rdf:value and rdf:resource", kXMPErr_BadXMP );
1229
0
          hasValueAttr = true;
1230
0
          valueNode = *currAttr;
1231
7.68k
        } else if ( (*currAttr)->name != "xml:lang" ) {
1232
7.68k
          hasPropertyAttrs = true;
1233
7.68k
        }
1234
7.68k
        break;
1235
1236
7.68k
      default :
1237
0
        XMP_Throw ( "Unrecognized attribute of empty property element", kXMPErr_BadRDF );
1238
0
        break;
1239
1240
7.68k
    }
1241
1242
7.68k
  }
1243
  
1244
  // Create the right kind of child node and visit the attributes again to add the fields or qualifiers.
1245
  // ! Because of implementation vagaries, the xmpParent is the tree root for top level properties.
1246
  // ! The schema is found, created if necessary, by AddChildNode.
1247
  
1248
2.56k
  XMP_Node * childNode = AddChildNode ( xmpParent, xmlNode, "", isTopLevel );
1249
2.56k
  bool childIsStruct = false;
1250
  
1251
2.56k
  if ( hasValueAttr | hasResourceAttr ) {
1252
0
    childNode->value = valueNode->value;
1253
0
    if ( ! hasValueAttr ) childNode->options |= kXMP_PropValueIsURI; // ! Might have both rdf:value and rdf:resource.
1254
2.56k
  } else if ( hasPropertyAttrs ) {
1255
2.55k
    childNode->options |= kXMP_PropValueIsStruct;
1256
2.55k
    childIsStruct = true;
1257
2.55k
  }
1258
    
1259
2.56k
  currAttr = xmlNode.attrs.begin();
1260
2.56k
  endAttr  = xmlNode.attrs.end();
1261
1262
10.2k
  for ( ; currAttr != endAttr; ++currAttr ) {
1263
1264
7.67k
    if ( *currAttr == valueNode ) continue; // Skip the rdf:value or rdf:resource attribute holding the value.
1265
7.67k
    RDFTermKind attrTerm = GetRDFTermKind ( (*currAttr)->name );
1266
1267
7.67k
    switch ( attrTerm ) {
1268
1269
0
      case kRDFTerm_ID       :
1270
0
      case kRDFTerm_nodeID   :
1271
0
        break; // Ignore all rdf:ID and rdf:nodeID attributes.w
1272
        
1273
0
      case kRDFTerm_resource :
1274
0
        AddQualifierNode ( childNode, **currAttr );
1275
0
        break;
1276
1277
7.67k
      case kRDFTerm_Other :
1278
7.67k
        if ( (! childIsStruct) || (*currAttr)->name == "xml:lang" ) {
1279
0
          AddQualifierNode ( childNode, **currAttr );
1280
7.67k
        } else {
1281
7.67k
          AddChildNode ( childNode, **currAttr, (*currAttr)->value.c_str(), false );
1282
7.67k
        }
1283
7.67k
        break;
1284
1285
0
      default :
1286
0
        XMP_Throw ( "Unrecognized attribute of empty property element", kXMPErr_BadRDF );
1287
0
        break;
1288
1289
7.67k
    }
1290
1291
7.67k
  }
1292
1293
2.56k
}  // RDF_EmptyPropertyElement
1294
1295
1296
// =================================================================================================