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