/src/exiv2/xmpsdk/src/XML_Node.cpp
Line | Count | Source |
1 | | // ================================================================================================= |
2 | | // Copyright 2005-2007 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" // ! Must be the first #include! |
10 | | #include "XMLParserAdapter.hpp" |
11 | | |
12 | | #include <map> |
13 | | #include <cstring> |
14 | | #include <cstdio> |
15 | | |
16 | | // ! Can't include XMP..._Impl.hpp - used by both Core and Files. |
17 | 0 | #define XMP_LitNMatch(s,l,n) (std::strncmp((s),(l),(n)) == 0) |
18 | | |
19 | | #if XMP_WinBuild |
20 | | #ifdef _MSC_VER |
21 | | #define snprintf _snprintf |
22 | | #pragma warning ( disable : 4996 ) // snprintf is safe |
23 | | #endif |
24 | | #endif |
25 | | |
26 | | // ================================================================================================= |
27 | | |
28 | | #if 0 // Pattern for iterating over the children or attributes: |
29 | | for ( size_t xxNum = 0, xxLim = _node_->_offspring_->size(); xxNum < xxLim; ++xxNum ) { |
30 | | const XML_NodePtr _curr_ = _node_->_offspring_[xxNum]; |
31 | | } |
32 | | #endif |
33 | | |
34 | | // ================================================================================================= |
35 | | // XML_Node::IsWhitespaceNode |
36 | | //=========================== |
37 | | |
38 | | bool XML_Node::IsWhitespaceNode() const |
39 | 252k | { |
40 | 252k | if ( this->kind != kCDataNode ) return false; |
41 | | |
42 | 657k | for ( size_t i = 0; i < this->value.size(); ++i ) { |
43 | 471k | unsigned char ch = this->value[i]; |
44 | 471k | if ( IsWhitespaceChar ( ch ) ) continue; |
45 | | // *** Add checks for other whitespace characters. |
46 | 358 | return false; // All the checks failed, this isn't whitespace. |
47 | 471k | } |
48 | | |
49 | 186k | return true; |
50 | | |
51 | 187k | } // XML_Node::IsWhitespaceNode |
52 | | |
53 | | // ================================================================================================= |
54 | | // XML_Node::IsLeafContentNode |
55 | | //============================ |
56 | | |
57 | | bool XML_Node::IsLeafContentNode() const |
58 | 0 | { |
59 | 0 | if ( this->kind != kElemNode ) return false; |
60 | 0 | if ( this->content.size() == 0 ) return true; |
61 | 0 | if ( this->content.size() > 1 ) return false; |
62 | 0 | if ( this->content[0]->kind != kCDataNode ) return false; |
63 | | |
64 | 0 | return true; |
65 | |
|
66 | 0 | } // XML_Node::IsLeafContentNode |
67 | | |
68 | | // ================================================================================================= |
69 | | // XML_Node::IsEmptyLeafNode |
70 | | //========================== |
71 | | |
72 | | bool XML_Node::IsEmptyLeafNode() const |
73 | 0 | { |
74 | |
|
75 | 0 | if ( (this->kind != kElemNode) || (this->content.size() != 0) ) return false; |
76 | 0 | return true; |
77 | |
|
78 | 0 | } // XML_Node::IsEmptyLeafNode |
79 | | |
80 | | // ================================================================================================= |
81 | | // XML_Node::GetAttrValue |
82 | | //======================= |
83 | | |
84 | | XMP_StringPtr XML_Node::GetAttrValue ( XMP_StringPtr attrName ) const |
85 | 0 | { |
86 | |
|
87 | 0 | for ( size_t i = 0, aLim = this->attrs.size(); i < aLim; ++i ) { |
88 | 0 | XML_Node * attrPtr = this->attrs[i]; |
89 | 0 | if ( ! attrPtr->ns.empty() ) continue; // This form of GetAttrValue is for attrs in no namespace. |
90 | 0 | if ( attrPtr->name == attrName ) return attrPtr->value.c_str(); |
91 | 0 | } |
92 | | |
93 | 0 | return 0; // Not found. |
94 | |
|
95 | 0 | } // XML_Node::GetAttrValue |
96 | | |
97 | | // ================================================================================================= |
98 | | // XML_Node::SetAttrValue |
99 | | //======================= |
100 | | |
101 | | void XML_Node::SetAttrValue ( XMP_StringPtr attrName, XMP_StringPtr attrValue ) |
102 | 0 | { |
103 | | |
104 | 0 | for ( size_t i = 0, aLim = this->attrs.size(); i < aLim; ++i ) { |
105 | 0 | XML_Node * attrPtr = this->attrs[i]; |
106 | 0 | if ( ! attrPtr->ns.empty() ) continue; // This form of SetAttrValue is for attrs in no namespace. |
107 | 0 | if ( attrPtr->name == attrName ) { |
108 | 0 | attrPtr->value = attrValue; |
109 | 0 | return; |
110 | 0 | } |
111 | 0 | } |
112 | |
|
113 | 0 | } // XML_Node::SetAttrValue |
114 | | |
115 | | // ================================================================================================= |
116 | | // XML_Node::GetLeafContentValue |
117 | | //============================== |
118 | | |
119 | | XMP_StringPtr XML_Node::GetLeafContentValue() const |
120 | 0 | { |
121 | 0 | if ( (! this->IsLeafContentNode()) || this->content.empty() ) return ""; |
122 | | |
123 | 0 | return this->content[0]->value.c_str(); |
124 | |
|
125 | 0 | } // XML_Node::GetLeafContentValue |
126 | | |
127 | | // ================================================================================================= |
128 | | // XML_Node::SetLeafContentValue |
129 | | //============================== |
130 | | |
131 | | void XML_Node::SetLeafContentValue ( XMP_StringPtr newValue ) |
132 | 0 | { |
133 | 0 | XML_Node * valueNode; |
134 | | |
135 | 0 | if ( ! this->content.empty() ) { |
136 | 0 | valueNode = this->content[0]; |
137 | 0 | } else { |
138 | 0 | valueNode = new XML_Node ( this, "", kCDataNode ); |
139 | 0 | this->content.push_back ( valueNode ); |
140 | 0 | } |
141 | |
|
142 | 0 | valueNode->value = newValue; |
143 | |
|
144 | 0 | } // XML_Node::SetLeafContentValue |
145 | | |
146 | | // ================================================================================================= |
147 | | // XML_Node::CountNamedElements |
148 | | //============================= |
149 | | |
150 | | size_t XML_Node::CountNamedElements ( XMP_StringPtr nsURI, XMP_StringPtr localName ) const |
151 | 0 | { |
152 | 0 | size_t count = 0; |
153 | | |
154 | 0 | for ( size_t i = 0, vLim = this->content.size(); i < vLim; ++i ) { |
155 | 0 | const XML_Node & child = *this->content[i]; |
156 | 0 | if ( child.ns != nsURI ) continue; |
157 | 0 | if ( strcmp ( localName, child.name.c_str()+child.nsPrefixLen ) != 0 ) continue; |
158 | 0 | ++count; |
159 | 0 | } |
160 | | |
161 | 0 | return count; |
162 | | |
163 | 0 | } // XML_Node::CountNamedElements |
164 | | |
165 | | // ================================================================================================= |
166 | | // XML_Node::GetNamedElement |
167 | | //========================== |
168 | | |
169 | | XML_NodePtr XML_Node::GetNamedElement ( XMP_StringPtr nsURI, XMP_StringPtr localName, size_t which /* = 0 */ ) |
170 | 0 | { |
171 | |
|
172 | 0 | for ( size_t i = 0, vLim = this->content.size(); i < vLim; ++i ) { |
173 | 0 | XML_Node * childPtr = this->content[i]; |
174 | 0 | if ( childPtr->ns != nsURI ) continue; |
175 | 0 | if ( strcmp ( localName, childPtr->name.c_str()+childPtr->nsPrefixLen ) != 0 ) continue; |
176 | 0 | if ( which == 0 ) return childPtr; |
177 | 0 | --which; |
178 | 0 | } |
179 | | |
180 | 0 | return 0; /// Not found. |
181 | |
|
182 | 0 | } // XML_Node::GetNamedElement |
183 | | |
184 | | // ================================================================================================= |
185 | | // DumpNodeList |
186 | | // ============ |
187 | | |
188 | | static const char * kNodeKinds[] = { "root", "elem", "attr", "cdata", "pi" }; |
189 | | |
190 | | static void DumpNodeList ( std::string * buffer, const XML_NodeVector & list, int indent ) |
191 | 0 | { |
192 | |
|
193 | 0 | for ( size_t i = 0, limit = list.size(); i < limit; ++i ) { |
194 | |
|
195 | 0 | const XML_Node * node = list[i]; |
196 | | |
197 | 0 | for ( int t = indent; t > 0; --t ) *buffer += " "; |
198 | 0 | if ( node->IsWhitespaceNode() ) { |
199 | 0 | *buffer += "-- whitespace --\n"; |
200 | 0 | continue; |
201 | 0 | } |
202 | | |
203 | 0 | *buffer += node->name; |
204 | 0 | *buffer += " - "; |
205 | 0 | *buffer += kNodeKinds[node->kind]; |
206 | 0 | if ( ! node->value.empty() ) { |
207 | 0 | *buffer += ", value=\""; |
208 | 0 | *buffer += node->value; |
209 | 0 | *buffer += "\""; |
210 | 0 | } |
211 | 0 | if ( ! node->ns.empty() ) { |
212 | 0 | *buffer += ", ns=\""; |
213 | 0 | *buffer += node->ns; |
214 | 0 | *buffer += "\""; |
215 | 0 | } |
216 | 0 | if ( node->nsPrefixLen != 0 ) { |
217 | 0 | *buffer += ", prefixLen="; |
218 | 0 | char numBuf [20]; |
219 | 0 | snprintf ( numBuf, sizeof(numBuf), "%lu", (unsigned long)node->nsPrefixLen ); |
220 | 0 | *buffer += numBuf; |
221 | 0 | } |
222 | 0 | *buffer += "\n"; |
223 | | |
224 | 0 | if ( ! node->attrs.empty() ) { |
225 | 0 | for ( int t = indent+1; t > 0; --t ) *buffer += " "; |
226 | 0 | *buffer += "attrs:\n"; |
227 | 0 | DumpNodeList ( buffer, node->attrs, indent+2 ); |
228 | 0 | } |
229 | | |
230 | 0 | if ( ! node->content.empty() ) { |
231 | 0 | DumpNodeList ( buffer, node->content, indent+1 ); |
232 | 0 | } |
233 | |
|
234 | 0 | } |
235 | |
|
236 | 0 | } // DumpNodeList |
237 | | |
238 | | // ================================================================================================= |
239 | | // XML_Node::Dump |
240 | | //=============== |
241 | | |
242 | | void XML_Node::Dump ( std::string * buffer ) |
243 | 0 | { |
244 | |
|
245 | 0 | *buffer = "Dump of XML_Node tree\n"; |
246 | |
|
247 | 0 | *buffer += "Root info: name=\""; |
248 | 0 | *buffer += this->name; |
249 | 0 | *buffer += "\", value=\""; |
250 | 0 | *buffer += this->value; |
251 | 0 | *buffer += "\", ns=\""; |
252 | 0 | *buffer += this->ns; |
253 | 0 | *buffer += "\", kind="; |
254 | 0 | *buffer += kNodeKinds[this->kind]; |
255 | 0 | *buffer += "\n"; |
256 | | |
257 | 0 | if ( ! this->attrs.empty() ) { |
258 | 0 | *buffer += " attrs:\n"; |
259 | 0 | DumpNodeList ( buffer, this->attrs, 2 ); |
260 | 0 | } |
261 | 0 | *buffer += "\n"; |
262 | | |
263 | 0 | DumpNodeList ( buffer, this->content, 0 ); |
264 | |
|
265 | 0 | } // XML_Node::Dump |
266 | | |
267 | | // ================================================================================================= |
268 | | // SerializeOneNode |
269 | | // ================ |
270 | | |
271 | | static void SerializeOneNode ( std::string * buffer, const XML_Node & node ) |
272 | 0 | { |
273 | 0 | size_t i, limit; |
274 | 0 | XMP_StringPtr namePtr = node.name.c_str(); |
275 | 0 | if ( XMP_LitNMatch ( namePtr, "_dflt_:", 7 ) ) namePtr += 7; // Hack for default namespaces. |
276 | |
|
277 | 0 | switch ( node.kind ) { |
278 | | |
279 | 0 | case kElemNode: |
280 | 0 | *buffer += '<'; |
281 | 0 | *buffer += namePtr; |
282 | 0 | for ( i = 0, limit = node.attrs.size(); i < limit; ++i ) { |
283 | 0 | SerializeOneNode ( buffer, *node.attrs[i] ); |
284 | 0 | } |
285 | 0 | if ( node.content.empty() ) { |
286 | 0 | *buffer += "/>"; |
287 | 0 | } else { |
288 | 0 | *buffer += '>'; |
289 | 0 | for ( i = 0, limit = node.content.size(); i < limit; ++i ) { |
290 | 0 | SerializeOneNode ( buffer, *node.content[i] ); |
291 | 0 | } |
292 | 0 | *buffer += "</"; |
293 | 0 | *buffer += namePtr; |
294 | 0 | *buffer += '>'; |
295 | 0 | } |
296 | 0 | break; |
297 | | |
298 | 0 | case kAttrNode: |
299 | 0 | *buffer += ' '; |
300 | 0 | *buffer += namePtr; |
301 | 0 | *buffer += "=\""; |
302 | 0 | *buffer += node.value; |
303 | 0 | *buffer += '"'; |
304 | 0 | break; |
305 | | |
306 | 0 | case kCDataNode: |
307 | 0 | *buffer += node.value; |
308 | 0 | break; |
309 | | |
310 | 0 | case kPINode: |
311 | 0 | *buffer += node.value; // *** Note that we're dropping PIs during the Expat parse. |
312 | 0 | break; |
313 | | |
314 | 0 | } |
315 | |
|
316 | 0 | } // SerializeOneNode |
317 | | |
318 | | // ================================================================================================= |
319 | | // CollectNamespaceDecls |
320 | | // ===================== |
321 | | |
322 | | typedef std::map < std::string, std::string > NamespaceMap; |
323 | | |
324 | | static void CollectNamespaceDecls ( NamespaceMap * nsMap, const XML_Node & node ) |
325 | 0 | { |
326 | 0 | size_t i, limit; |
327 | |
|
328 | 0 | if ( ! node.ns.empty() ) { |
329 | 0 | size_t nameMid = 0; |
330 | 0 | while ( node.name[nameMid] != ':' ) ++nameMid; |
331 | 0 | std::string prefix = node.name.substr ( 0, nameMid ); |
332 | 0 | (*nsMap)[prefix] = node.ns; |
333 | 0 | } |
334 | | |
335 | 0 | if ( node.kind == kElemNode ) { |
336 | |
|
337 | 0 | for ( i = 0, limit = node.attrs.size(); i < limit; ++i ) { |
338 | 0 | CollectNamespaceDecls ( nsMap, *node.attrs[i] ); |
339 | 0 | } |
340 | |
|
341 | 0 | for ( i = 0, limit = node.content.size(); i < limit; ++i ) { |
342 | 0 | const XML_Node & content = *node.content[i]; |
343 | 0 | if ( content.kind == kElemNode ) CollectNamespaceDecls ( nsMap, content ); |
344 | 0 | } |
345 | |
|
346 | 0 | } |
347 | | |
348 | 0 | } // CollectNamespaceDecls |
349 | | |
350 | | // ================================================================================================= |
351 | | // XML_Node::Serialize |
352 | | //==================== |
353 | | |
354 | | void XML_Node::Serialize ( std::string * buffer ) |
355 | 0 | { |
356 | 0 | buffer->erase(); |
357 | | |
358 | 0 | if ( this->kind != kRootNode ) { |
359 | |
|
360 | 0 | SerializeOneNode ( buffer, *this ); |
361 | |
|
362 | 0 | } else { |
363 | | |
364 | | // Do the outermost level here, in order to add the XML version and namespace declarations. |
365 | |
|
366 | 0 | *buffer += "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"; |
367 | |
|
368 | 0 | for ( size_t outer = 0, oLimit = this->content.size(); outer < oLimit; ++outer ) { |
369 | |
|
370 | 0 | const XML_Node & node = *this->content[outer]; |
371 | |
|
372 | 0 | if ( node.kind != kElemNode ) { |
373 | |
|
374 | 0 | SerializeOneNode ( buffer, node ); |
375 | |
|
376 | 0 | } else { |
377 | |
|
378 | 0 | XMP_StringPtr namePtr = node.name.c_str(); |
379 | 0 | if ( XMP_LitNMatch ( namePtr, "_dflt_:", 7 ) ) namePtr += 7; // Hack for default namespaces. |
380 | |
|
381 | 0 | *buffer += '<'; |
382 | 0 | *buffer += namePtr; |
383 | | |
384 | 0 | NamespaceMap nsMap; |
385 | 0 | CollectNamespaceDecls ( &nsMap, node ); |
386 | 0 | NamespaceMap::iterator nsDecl = nsMap.begin(); |
387 | 0 | NamespaceMap::iterator nsEnd = nsMap.end(); |
388 | 0 | for ( ; nsDecl != nsEnd; ++nsDecl ) { |
389 | 0 | const std::string & prefix = nsDecl->first; |
390 | 0 | *buffer += " xmlns"; |
391 | 0 | if ( prefix != "_dflt_" ) { *buffer += ':'; *buffer += prefix; } |
392 | 0 | *buffer += "=\""; |
393 | 0 | *buffer += nsDecl->second; |
394 | 0 | *buffer += '"'; |
395 | 0 | } |
396 | |
|
397 | 0 | for ( size_t attr = 0, aLimit = node.attrs.size(); attr < aLimit; ++attr ) { |
398 | 0 | SerializeOneNode ( buffer, *node.attrs[attr] ); |
399 | 0 | } |
400 | |
|
401 | 0 | if ( node.content.empty() ) { |
402 | 0 | *buffer += "/>"; |
403 | 0 | } else { |
404 | 0 | *buffer += '>'; |
405 | 0 | for ( size_t child = 0, cLimit = node.content.size(); child < cLimit; ++child ) { |
406 | 0 | SerializeOneNode ( buffer, *node.content[child] ); |
407 | 0 | } |
408 | 0 | *buffer += "</"; |
409 | 0 | *buffer += namePtr; |
410 | 0 | *buffer += '>'; |
411 | 0 | } |
412 | |
|
413 | 0 | } |
414 | |
|
415 | 0 | } |
416 | | |
417 | 0 | } |
418 | | |
419 | |
|
420 | 0 | } // XML_Node::Serialize |
421 | | |
422 | | // ================================================================================================= |
423 | | // XML_Node::RemoveAttrs |
424 | | //====================== |
425 | | |
426 | | void XML_Node::RemoveAttrs() |
427 | 533k | { |
428 | | |
429 | 652k | for ( size_t i = 0, vLim = this->attrs.size(); i < vLim; ++i ) delete this->attrs[i]; |
430 | 533k | this->attrs.clear(); |
431 | | |
432 | 533k | } // XML_Node::RemoveAttrs |
433 | | |
434 | | // ================================================================================================= |
435 | | // XML_Node::RemoveContent |
436 | | //======================== |
437 | | |
438 | | void XML_Node::RemoveContent() |
439 | 533k | { |
440 | | |
441 | 947k | for ( size_t i = 0, vLim = this->content.size(); i < vLim; ++i ) delete this->content[i]; |
442 | 533k | this->content.clear(); |
443 | | |
444 | 533k | } // XML_Node::RemoveContent |
445 | | |
446 | | // ================================================================================================= |
447 | | // XML_Node::ClearNode |
448 | | //==================== |
449 | | |
450 | | void XML_Node::ClearNode() |
451 | 0 | { |
452 | |
|
453 | 0 | this->kind = 0; |
454 | 0 | this->ns.erase(); |
455 | 0 | this->name.erase(); |
456 | 0 | this->value.erase(); |
457 | |
|
458 | 0 | this->RemoveAttrs(); |
459 | 0 | this->RemoveContent(); |
460 | |
|
461 | 0 | } // XML_Node::ClearNode |
462 | | |
463 | | // ================================================================================================= |