/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 | | // ================================================================================================= |