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