Added headers
[tpot/pegasus/.git] / src / Pegasus / Common / XmlParser.cpp
1 //%/////////////////////////////////////////////////////////////////////////////\r
2 //\r
3 // Copyright (c) 2000, 2001 The Open group, BMC Software, Tivoli Systems, IBM\r
4 //\r
5 // Permission is hereby granted, free of charge, to any person obtaining a copy\r
6 // of this software and associated documentation files (the "Software"), to \r
7 // deal in the Software without restriction, including without limitation the \r
8 // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or \r
9 // sell copies of the Software, and to permit persons to whom the Software is\r
10 // furnished to do so, subject to the following conditions:\r
11 // \r
12 // THE ABOVE COPYRIGHT NOTICE AND THIS PERMISSION NOTICE SHALL BE INCLUDED IN \r
13 // ALL COPIES OR SUBSTANTIAL PORTIONS OF THE SOFTWARE. THE SOFTWARE IS PROVIDED\r
14 // "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT\r
15 // LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR \r
16 // PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT \r
17 // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN \r
18 // ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\r
19 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
20 //\r
21 //==============================================================================\r
22 //\r
23 // Author: Mike Brasher (mbrasher@bmc.com)\r
24 //\r
25 // Modified By:\r
26 //\r
27 //%/////////////////////////////////////////////////////////////////////////////\r
28 \r
29 ////////////////////////////////////////////////////////////////////////////////\r
30 //\r
31 // XmlParser\r
32 //\r
33 //      This file contains a simple non-validating XML parser. Here are \r
34 //      serveral rules for well-formed XML:\r
35 //\r
36 //          1.  Documents must begin with an XML declaration:\r
37 //\r
38 //              <?xml version="1.0" standalone="yes"?>\r
39 //\r
40 //          2.  Comments have the form:\r
41 //\r
42 //              <!-- blah blah blah -->\r
43 //\r
44 //          3. The following entity references are supported:\r
45 //\r
46 //              &amp - ampersand\r
47 //              &lt - less-than\r
48 //              &gt - greater-than\r
49 //              &quot - full quote\r
50 //              &apos - apostrophe\r
51 //\r
52 //          4. Element names and attribute names take the following form:\r
53 //\r
54 //              [A-Za-z_][A-Za-z_0-9-.:]\r
55 //\r
56 //          5.  Arbitrary data (CDATA) can be enclosed like this:\r
57 //\r
58 //                  <![CDATA[\r
59 //                  ...\r
60 //                  ]]>\r
61 //\r
62 //          6.  Element names and attributes names are case-sensitive.\r
63 //\r
64 //          7.  XmlAttribute values must be delimited by full or half quotes.\r
65 //              XmlAttribute values must be delimited.\r
66 //\r
67 //          8.  <!DOCTYPE...>\r
68 //\r
69 // TODO:\r
70 //\r
71 //      Handle <!DOCTYPE...> sections which are complicated (containing\r
72 //        rules rather than references to files).\r
73 //\r
74 //      Handle reference of this form: "&#913;"\r
75 //\r
76 //      Remove newlines from string literals:\r
77 //\r
78 //          Example: <xyz x="hello\r
79 //              world">\r
80 //\r
81 ////////////////////////////////////////////////////////////////////////////////\r
82 \r
83 #include <cctype>\r
84 #include <cassert>\r
85 #include <cstdio>\r
86 #include <cstdlib>\r
87 #include <cstring>\r
88 #include "XmlParser.h"\r
89 #include "Logger.h"\r
90 \r
91 PEGASUS_NAMESPACE_BEGIN\r
92 \r
93 #define PEGASUS_ARRAY_T XmlEntry\r
94 # include "ArrayImpl.h"\r
95 #undef PEGASUS_ARRAY_T\r
96 \r
97 \r
98 ////////////////////////////////////////////////////////////////////////////////\r
99 //\r
100 // Static helper functions\r
101 //\r
102 ////////////////////////////////////////////////////////////////////////////////\r
103 \r
104 static void _printValue(const char* p)\r
105 {\r
106     for (; *p; p++)\r
107     {\r
108         if (*p == '\n')\r
109             PEGASUS_STD(cout) << "\\n";\r
110         else if (*p == '\r')\r
111             PEGASUS_STD(cout) << "\\r";\r
112         else if (*p == '\t')\r
113             PEGASUS_STD(cout) << "\\t";\r
114         else\r
115             PEGASUS_STD(cout) << *p;\r
116     }\r
117 }\r
118 \r
119 struct EntityReference\r
120 {\r
121     const char* match;\r
122     Uint32 length;\r
123     char replacement;\r
124 };\r
125 \r
126 static EntityReference _references[] =\r
127 {\r
128     { "&amp;", 5, '&' },\r
129     { "&lt;", 4, '<' },\r
130     { "&gt;", 4, '>' },\r
131     { "&quot;", 6, '"' },\r
132     { "&apos;", 6, '\'' }\r
133 };\r
134 \r
135 static Uint32 _REFERENCES_SIZE = (sizeof(_references) / sizeof(_references[0]));\r
136 \r
137 // Remove all redundant spaces from the given string:\r
138 \r
139 static void _normalize(char* text)\r
140 {\r
141     Uint32 length = strlen(text);\r
142     char* p = text;\r
143     char* end = p + length;\r
144 \r
145     // Remove leading spaces:\r
146 \r
147     while (isspace(*p))\r
148         p++;\r
149 \r
150     if (p != text)\r
151         memmove(text, p, end - p + 1);\r
152 \r
153     p = text;\r
154 \r
155     // Look for sequences of more than one space and remove all but one.\r
156 \r
157     for (;;)\r
158     {\r
159         // Advance to the next space:\r
160 \r
161         while (*p && !isspace(*p))\r
162             p++;\r
163 \r
164         if (!*p)\r
165             break;\r
166 \r
167         // Advance to the next non-space:\r
168 \r
169         char* q = p++;\r
170 \r
171         while (isspace(*p))\r
172             p++;\r
173 \r
174         // Discard trailing spaces (if we are at the end):\r
175 \r
176         if (!*p)\r
177         {\r
178             *q = '\0';\r
179             break;\r
180         }\r
181 \r
182         // Remove the redundant spaces:\r
183 \r
184         Uint32 n = p - q;\r
185 \r
186         if (n > 1)\r
187         {\r
188             *q++ = ' ';\r
189             memmove(q, p, end - p + 1);\r
190             p = q;\r
191         }\r
192     }\r
193 }\r
194 \r
195 ////////////////////////////////////////////////////////////////////////////////\r
196 //\r
197 // XmlException\r
198 //\r
199 ////////////////////////////////////////////////////////////////////////////////\r
200 \r
201 static const char* _xmlMessages[] =\r
202 {\r
203     "Bad opening element",\r
204     "Bad closing element",\r
205     "Bad attribute name",\r
206     "Exepected equal sign",\r
207     "Bad attribute value",\r
208     "A \"--\" sequence found within comment",\r
209     "Unterminated comment",\r
210     "Unterminated CDATA block",\r
211     "Unterminated DOCTYPE",\r
212     "Too many attributes: parser only handles 10",\r
213     "Malformed reference",\r
214     "Expected a comment or CDATA following \"<!\" sequence",\r
215     "Closing element does not match opening element",\r
216     "One or more tags are still open",\r
217     "More than one root element was encountered",\r
218     "Validation error",\r
219     "Semantic error"\r
220 };\r
221 \r
222 static String _formMessage(Uint32 code, Uint32 line, const String& message)\r
223 {\r
224     String result = _xmlMessages[Uint32(code) - 1];\r
225 \r
226     char buffer[32];\r
227     sprintf(buffer, "%d", line);\r
228     result.append(": on line ");\r
229     result.append(buffer);\r
230 \r
231     if (message.size())\r
232     {\r
233         result.append(": ");\r
234         result.append(message);\r
235     }\r
236 \r
237     return result;\r
238 }\r
239 \r
240 XmlException::XmlException(\r
241     XmlException::Code code, \r
242     Uint32 lineNumber,\r
243     const String& message) \r
244     : Exception(_formMessage(code, lineNumber, message))\r
245 {\r
246 \r
247 }\r
248 \r
249 ////////////////////////////////////////////////////////////////////////////////\r
250 //\r
251 // XmlValidationError\r
252 //\r
253 ////////////////////////////////////////////////////////////////////////////////\r
254 \r
255 XmlValidationError::XmlValidationError(\r
256     Uint32 lineNumber,\r
257     const String& message)\r
258     : XmlException(XmlException::VALIDATION_ERROR, lineNumber, message)\r
259 {\r
260 \r
261 }\r
262 \r
263 ////////////////////////////////////////////////////////////////////////////////\r
264 //\r
265 // XmlSemanticError\r
266 //\r
267 ////////////////////////////////////////////////////////////////////////////////\r
268 \r
269 XmlSemanticError::XmlSemanticError(\r
270     Uint32 lineNumber,\r
271     const String& message)\r
272     : XmlException(XmlException::SEMANTIC_ERROR, lineNumber, message)\r
273 {\r
274 \r
275 }\r
276 \r
277 ////////////////////////////////////////////////////////////////////////////////\r
278 //\r
279 // XmlParser\r
280 //\r
281 ////////////////////////////////////////////////////////////////////////////////\r
282 \r
283 XmlParser::XmlParser(char* text) : _line(1), _text(text), _current(text), \r
284     _restoreChar('\0'), _foundRoot(false)\r
285 {\r
286 \r
287 }\r
288 \r
289 Boolean XmlParser::next(XmlEntry& entry)\r
290 {\r
291     if (!_putBackStack.isEmpty())\r
292     {\r
293         entry = _putBackStack.top();\r
294         _putBackStack.pop();\r
295         return true;\r
296     }\r
297 \r
298     // If a character was overwritten with a null-terminator the last\r
299     // time this routine was called, then put back that character. Before\r
300     // exiting of course, restore the null-terminator.\r
301 \r
302     char* nullTerminator = 0;\r
303 \r
304     if (_restoreChar && !*_current)\r
305     {\r
306         nullTerminator = _current;\r
307         *_current = _restoreChar;\r
308         _restoreChar = '\0';\r
309     }\r
310 \r
311     // Skip over any whitespace:\r
312 \r
313     _skipWhitespace(_current);\r
314 \r
315     if (!*_current)\r
316     {\r
317         if (nullTerminator)\r
318             *nullTerminator = '\0';\r
319 \r
320         if (!_stack.isEmpty())\r
321             throw XmlException(XmlException::UNCLOSED_TAGS, _line);\r
322 \r
323         return false;\r
324     }\r
325 \r
326     // Either a "<...>" or content begins next:\r
327 \r
328     if (*_current == '<')\r
329     {\r
330         _current++;\r
331         _getElement(_current, entry);\r
332 \r
333         if (nullTerminator)\r
334             *nullTerminator = '\0';\r
335 \r
336         if (entry.type == XmlEntry::START_TAG)\r
337         {\r
338             if (_stack.isEmpty() && _foundRoot)\r
339                 throw XmlException(XmlException::MULTIPLE_ROOTS, _line);\r
340 \r
341             _foundRoot = true;\r
342             _stack.push((char*)entry.text);\r
343         }\r
344         else if (entry.type == XmlEntry::END_TAG)\r
345         {\r
346             if (_stack.isEmpty())\r
347                 throw XmlException(XmlException::START_END_MISMATCH, _line);\r
348 \r
349             if (strcmp(_stack.top(), entry.text) != 0)\r
350                 throw XmlException(XmlException::START_END_MISMATCH, _line);\r
351 \r
352             _stack.pop();\r
353         }\r
354 \r
355         return true;\r
356     }\r
357     else\r
358     {\r
359         entry.type = XmlEntry::CONTENT;\r
360         entry.text = _current;\r
361         _getContent(_current);\r
362         _restoreChar = *_current;\r
363         *_current = '\0';\r
364 \r
365         if (nullTerminator)\r
366             *nullTerminator = '\0';\r
367 \r
368         _substituteReferences((char*)entry.text);\r
369         _normalize((char*)entry.text);\r
370 \r
371         return true;\r
372     }\r
373 }\r
374 \r
375 void XmlParser::putBack(XmlEntry& entry)\r
376 {\r
377     _putBackStack.push(entry);\r
378 }\r
379 \r
380 XmlParser::~XmlParser()\r
381 {\r
382     // Nothing to do!\r
383 }\r
384 \r
385 void XmlParser::_skipWhitespace(char*& p)\r
386 {\r
387     while (*p && isspace(*p))\r
388     {\r
389         if (*p == '\n')\r
390             _line++;\r
391 \r
392         p++;\r
393     }\r
394 }\r
395 \r
396 Boolean XmlParser::_getElementName(char*& p)\r
397 {\r
398     if (!isalpha(*p) && *p != '_')\r
399         throw XmlException(XmlException::BAD_START_TAG, _line);\r
400 \r
401     while (*p && \r
402         (isalnum(*p) || *p == '_' || *p == '-' || *p == ':' || *p == '.'))\r
403         p++;\r
404 \r
405     // The next character must be a space:\r
406 \r
407     if (isspace(*p))\r
408     {\r
409         *p++ = '\0';\r
410         _skipWhitespace(p);\r
411     }\r
412 \r
413     if (*p == '>')\r
414     {\r
415         *p++ = '\0';\r
416         return true;\r
417     }\r
418 \r
419     return false;\r
420 }\r
421 \r
422 Boolean XmlParser::_getOpenElementName(char*& p, Boolean& openCloseElement)\r
423 {\r
424     openCloseElement = false;\r
425 \r
426     if (!isalpha(*p) && *p != '_')\r
427         throw XmlException(XmlException::BAD_START_TAG, _line);\r
428 \r
429     while (*p && \r
430         (isalnum(*p) || *p == '_' || *p == '-' || *p == ':' || *p == '.'))\r
431         p++;\r
432 \r
433     // The next character must be a space:\r
434 \r
435     if (isspace(*p))\r
436     {\r
437         *p++ = '\0';\r
438         _skipWhitespace(p);\r
439     }\r
440 \r
441     if (*p == '>')\r
442     {\r
443         *p++ = '\0';\r
444         return true;\r
445     }\r
446 \r
447     if (p[0] == '/' && p[1] == '>')\r
448     {\r
449         openCloseElement = true;\r
450         *p = '\0';\r
451         p += 2;\r
452         return true;\r
453     }\r
454 \r
455     return false;\r
456 }\r
457 \r
458 void XmlParser::_getAttributeNameAndEqual(char*& p)\r
459 {\r
460     if (!isalpha(*p) && *p != '_')\r
461         throw XmlException(XmlException::BAD_ATTRIBUTE_NAME, _line);\r
462 \r
463     while (*p && \r
464         (isalnum(*p) || *p == '_' || *p == '-' || *p == ':' || *p == '.'))\r
465         p++;\r
466 \r
467     char* term = p;\r
468 \r
469     _skipWhitespace(p);\r
470 \r
471     if (*p != '=')\r
472         throw XmlException(XmlException::BAD_ATTRIBUTE_NAME, _line);\r
473 \r
474     p++;\r
475 \r
476     _skipWhitespace(p);\r
477 \r
478     *term = '\0';\r
479 }\r
480 \r
481 void XmlParser::_getAttributeValue(char*& p)\r
482 {\r
483     // ATTN-B: handle values contained in semiquotes:\r
484 \r
485     if (*p != '"' && *p != '\'')\r
486         throw XmlException(XmlException::BAD_ATTRIBUTE_VALUE, _line);\r
487 \r
488     char startChar = *p++;\r
489 \r
490     while (*p && *p != startChar)\r
491         p++;\r
492 \r
493     if (*p != startChar)\r
494         throw XmlException(XmlException::BAD_ATTRIBUTE_VALUE, _line);\r
495 \r
496     *p++ = '\0';\r
497 }\r
498 \r
499 void XmlParser::_getComment(char*& p)\r
500 {\r
501     // Now p points to first non-whitespace character beyond "<--" sequence:\r
502 \r
503     for (; *p; p++)\r
504     {\r
505         if (p[0] == '-' && p[1] == '-')\r
506         {\r
507             if (p[2] != '>')\r
508             {\r
509                 throw XmlException(\r
510                     XmlException::MINUS_MINUS_IN_COMMENT, _line);\r
511             }\r
512 \r
513             // Find end of comment (excluding whitespace):\r
514 \r
515             *p = '\0';\r
516             p += 3;\r
517             return;\r
518         }\r
519     }\r
520 \r
521     // If it got this far, then the comment is unterminated:\r
522 \r
523     throw XmlException(XmlException::UNTERMINATED_COMMENT, _line);\r
524 }\r
525 \r
526 void XmlParser::_getCData(char*& p)\r
527 {\r
528     // At this point p points one past "<![CDATA[" sequence:\r
529 \r
530     for (; *p; p++)\r
531     {\r
532         if (p[0] == ']' && p[1] == ']' && p[2] == '>')\r
533         {\r
534             *p = '\0';\r
535             p += 3;\r
536             return;\r
537         }\r
538         else if (*p == '\n')\r
539             _line++;\r
540     }\r
541 \r
542     // If it got this far, then the comment is unterminated:\r
543 \r
544     throw XmlException(XmlException::UNTERMINATED_CDATA, _line);\r
545 }\r
546 \r
547 void XmlParser::_getDocType(char*& p)\r
548 {\r
549     // Just ignore the DOCTYPE command for now:\r
550 \r
551     for (; *p && *p != '>'; p++)\r
552     {\r
553         if (*p == '\n')\r
554             _line++;\r
555     }\r
556 \r
557     if (*p != '>')\r
558         throw XmlException(XmlException::UNTERMINATED_DOCTYPE, _line);\r
559 \r
560     p++;\r
561 }\r
562 \r
563 void XmlParser::_getContent(char*& p)\r
564 {\r
565     while (*p && *p != '<')\r
566     {\r
567         if (*p == '\n')\r
568             _line++;\r
569 \r
570         p++;\r
571     }\r
572 }\r
573 \r
574 void XmlParser::_substituteReferences(char* text)\r
575 {\r
576     Uint32 rem = strlen(text);\r
577 \r
578     for (char* p = text; *p; p++, rem--)\r
579     {\r
580         if (*p == '&')\r
581         {\r
582             // Look for predefined entity reference:\r
583 \r
584             Boolean found = false;\r
585 \r
586             for (Uint32 i = 0; i < _REFERENCES_SIZE; i++)\r
587             {\r
588                 Uint32 length = _references[i].length;\r
589                 const char* match = _references[i].match;\r
590 \r
591                 if (strncmp(p, _references[i].match, length) == 0)\r
592                 {\r
593                     found = true;\r
594                     *p = _references[i].replacement;\r
595                     char* q = p + length;\r
596                     rem = rem - length + 1;\r
597                     memmove(p + 1, q, rem);\r
598                 }\r
599             }\r
600 \r
601             // If not found, then at least make sure it is well formed:\r
602 \r
603             if (!found)\r
604             {\r
605                 char* start = p;\r
606                 p++;\r
607 \r
608                 XmlException::Code code = XmlException::MALFORMED_REFERENCE;\r
609 \r
610                 if (isalpha(*p) || *p == '_')\r
611                 {\r
612                     for (p++; *p && *p != ';'; p++)\r
613                     {\r
614                         if (!isalnum(*p) && *p != '_')\r
615                             throw XmlException(code, _line);\r
616                     }\r
617                 }\r
618                 else if (*p == '#')\r
619                 {\r
620                     for (p++ ; *p && *p != ';'; p++)\r
621                     {\r
622                         if (!isdigit(*p))\r
623                             throw XmlException(code, _line);\r
624                     } \r
625                 }\r
626 \r
627                 if (*p != ';')\r
628                     throw XmlException(code, _line);\r
629 \r
630                 rem -= p - start;\r
631             }\r
632         }\r
633     }\r
634 }\r
635 \r
636 static const char _EMPTY_STRING[] = "";\r
637 \r
638 void XmlParser::_getElement(char*& p, XmlEntry& entry)\r
639 {\r
640     entry.attributeCount = 0;\r
641 \r
642     //--------------------------------------------------------------------------\r
643     // Get the element name (expect one of these: '?', '!', [A-Za-z_])\r
644     //--------------------------------------------------------------------------\r
645 \r
646     if (*p == '?')\r
647     {\r
648         entry.type = XmlEntry::XML_DECLARATION;\r
649         entry.text = ++p;\r
650 \r
651         Boolean openCloseElement = false;\r
652 \r
653         if (_getElementName(p))\r
654             return;\r
655     }\r
656     else if (*p == '!')\r
657     {\r
658         p++;\r
659 \r
660         // Expect a comment or CDATA:\r
661 \r
662         if (p[0] == '-' && p[1] == '-')\r
663         {\r
664             p += 2;\r
665             entry.type = XmlEntry::COMMENT;\r
666             entry.text = p;\r
667             _getComment(p);\r
668             return;\r
669         }\r
670         else if (memcmp(p, "[CDATA[", 7) == 0)\r
671         {\r
672             p += 7;\r
673             entry.type = XmlEntry::CDATA;\r
674             entry.text = p;\r
675             _getCData(p);\r
676             return;\r
677         }\r
678         else if (memcmp(p, "DOCTYPE", 7) == 0)\r
679         {\r
680             entry.type = XmlEntry::DOCTYPE;\r
681             entry.text = _EMPTY_STRING;\r
682             _getDocType(p);\r
683             return;\r
684         }\r
685         throw(XmlException(XmlException::EXPECTED_COMMENT_OR_CDATA, _line));\r
686     }\r
687     else if (*p == '/')\r
688     {\r
689         entry.type = XmlEntry::END_TAG;\r
690         entry.text = ++p;\r
691 \r
692         if (!_getElementName(p))\r
693             throw(XmlException(XmlException::BAD_END_TAG, _line));\r
694 \r
695         return;\r
696     }\r
697     else if (isalpha(*p) || *p == '_')\r
698     {\r
699         entry.type = XmlEntry::START_TAG;\r
700         entry.text = p;\r
701 \r
702         Boolean openCloseElement = false;\r
703 \r
704         if (_getOpenElementName(p, openCloseElement))\r
705         {\r
706             if (openCloseElement)\r
707                 entry.type = XmlEntry::EMPTY_TAG;\r
708             return;\r
709         }\r
710     }\r
711     else\r
712         throw XmlException(XmlException::BAD_START_TAG, _line);\r
713 \r
714     //--------------------------------------------------------------------------\r
715     // Grab all the attributes:\r
716     //--------------------------------------------------------------------------\r
717 \r
718     for (;;)\r
719     {\r
720         if (entry.type == XmlEntry::XML_DECLARATION)\r
721         {\r
722             if (p[0] == '?' && p[1] == '>')\r
723             {\r
724                 p += 2;\r
725                 return;\r
726             }\r
727         }\r
728         else if (entry.type == XmlEntry::START_TAG && p[0] == '/' && p[1] =='>')\r
729         {\r
730             entry.type = XmlEntry::EMPTY_TAG;\r
731             p += 2;\r
732             return;\r
733         }\r
734         else if (*p == '>')\r
735         {\r
736             p++;\r
737             return;\r
738         }\r
739 \r
740         XmlAttribute attr;\r
741         attr.name = p;\r
742         _getAttributeNameAndEqual(p);\r
743 \r
744         if (*p != '"' && *p != '\'')\r
745             throw XmlException(XmlException::BAD_ATTRIBUTE_VALUE, _line);\r
746 \r
747         attr.value = p + 1;\r
748         _getAttributeValue(p);\r
749 \r
750         if (entry.type == XmlEntry::XML_DECLARATION)\r
751         {\r
752             // The next thing must a space or a "?>":\r
753 \r
754             if (!(p[0] == '?' && p[1] == '>') && !isspace(*p))\r
755             {\r
756                 throw XmlException(\r
757                     XmlException::BAD_ATTRIBUTE_VALUE, _line);\r
758             }\r
759         }\r
760         else if (!(*p == '>' || (p[0] == '/' && p[1] == '>') || isspace(*p)))\r
761         {\r
762             // The next thing must be a space or a '>':\r
763 \r
764             throw XmlException(XmlException::BAD_ATTRIBUTE_VALUE, _line);\r
765         }\r
766 \r
767         _skipWhitespace(p);\r
768 \r
769         if (entry.attributeCount == XmlEntry::MAX_ATTRIBUTES)\r
770             throw XmlException(XmlException::TOO_MANY_ATTRIBUTES, _line);\r
771 \r
772         _substituteReferences((char*)attr.value);\r
773         entry.attributes[entry.attributeCount++] = attr;\r
774     }\r
775 }\r
776 \r
777 static const char* _typeStrings[] =\r
778 {\r
779     "XML_DECLARATION", \r
780     "START_TAG", \r
781     "EMPTY_TAG", \r
782     "END_TAG", \r
783     "COMMENT",\r
784     "CDATA",\r
785     "DOCTYPE",\r
786     "CONTENT" \r
787 };\r
788 \r
789 void XmlEntry::print() const\r
790 {\r
791     PEGASUS_STD(cout) << "=== " << _typeStrings[type] << " ";\r
792 \r
793     Boolean needQuotes = type == XmlEntry::CDATA || type == XmlEntry::CONTENT;\r
794 \r
795     if (needQuotes)\r
796         PEGASUS_STD(cout) << "\"";\r
797         \r
798     _printValue(text);\r
799 \r
800     if (needQuotes)\r
801         PEGASUS_STD(cout) << "\"";\r
802 \r
803     PEGASUS_STD(cout) << '\n';\r
804 \r
805     for (Uint32 i = 0; i < attributeCount; i++)\r
806     {\r
807         PEGASUS_STD(cout) << "    " << attributes[i].name << "=\"";\r
808         _printValue(attributes[i].value);\r
809         PEGASUS_STD(cout) << "\"" << PEGASUS_STD(endl);\r
810     }\r
811 }\r
812 \r
813 const XmlAttribute* XmlEntry::findAttribute(\r
814     const char* name) const\r
815 {\r
816     for (Uint32 i = 0; i < attributeCount; i++)\r
817     {\r
818         if (strcmp(attributes[i].name, name) == 0)\r
819             return &attributes[i];\r
820     }\r
821 \r
822     return 0;\r
823 }\r
824 \r
825 // Find first non-whitespace character (set first) and last non-whitespace\r
826 // character (set last one past this). For example, consider this string:\r
827 //\r
828 //      "   87     "\r
829 //\r
830 // The first pointer would point to '8' and the last pointer woudl point one\r
831 // beyond '7'.\r
832 \r
833 static void _findEnds(\r
834     const char* str, \r
835     const char*& first, \r
836     const char*& last)\r
837 {\r
838     first = str;\r
839 \r
840     while (isspace(*first))\r
841         first++;\r
842 \r
843     if (!*first)\r
844     {\r
845         last = first;\r
846         return;\r
847     }\r
848 \r
849     last = first + strlen(first);\r
850 \r
851     while (last != first && isspace(last[-1]))\r
852         last--;\r
853 }\r
854 \r
855 Boolean XmlEntry::getAttributeValue(\r
856     const char* name, \r
857     Uint32& value) const\r
858 {\r
859     const XmlAttribute* attr = findAttribute(name);\r
860 \r
861     if (!attr)\r
862         return false;\r
863 \r
864     const char* first;\r
865     const char* last;\r
866     _findEnds(attr->value, first, last);\r
867 \r
868     char* end = 0;\r
869     long tmp = strtol(first, &end, 10);\r
870 \r
871     if (!end || end != last)\r
872         return false;\r
873 \r
874     value = Uint32(tmp);\r
875     return true;\r
876 }\r
877 \r
878 Boolean XmlEntry::getAttributeValue(\r
879     const char* name, \r
880     Real32& value) const\r
881 {\r
882     const XmlAttribute* attr = findAttribute(name);\r
883 \r
884     if (!attr)\r
885         return false;\r
886 \r
887     const char* first;\r
888     const char* last;\r
889     _findEnds(attr->value, first, last);\r
890 \r
891     char* end = 0;\r
892     double tmp = strtod(first, &end);\r
893 \r
894     if (!end || end != last)\r
895         return false;\r
896 \r
897     value = Uint32(tmp);\r
898     return true;\r
899 }\r
900 \r
901 Boolean XmlEntry::getAttributeValue(\r
902     const char* name, \r
903     const char*& value) const\r
904 {\r
905     const XmlAttribute* attr = findAttribute(name);\r
906 \r
907     if (!attr)\r
908         return false;\r
909 \r
910     value = attr->value;\r
911     return true;\r
912 }\r
913 \r
914 Boolean XmlEntry::getAttributeValue(const char* name, String& value) const\r
915 {\r
916     const char* tmp;\r
917 \r
918     if (!getAttributeValue(name, tmp))\r
919         return false;\r
920 \r
921     value = tmp;\r
922     return true;\r
923 }\r
924 \r
925 void XmlAppendCString(Array<Sint8>& out, const char* str)\r
926 {\r
927     out.append(str, strlen(str));\r
928 }\r
929 \r
930 PEGASUS_NAMESPACE_END\r