]> git.saurik.com Git - wxWidgets.git/blame - utils/configtool/src/htmlparser.cpp
Oops, I left an extra { in there.
[wxWidgets.git] / utils / configtool / src / htmlparser.cpp
CommitLineData
d7463f75
JS
1/////////////////////////////////////////////////////////////////////////////
2// Name: htmlparser.cpp
3// Purpose: Simple HTML parser
4// Author: Julian Smart
5// Modified by:
6// Created: 2002-09-25
7// RCS-ID: $Id$
8// Copyright: (c) Julian Smart
9// Licence: wxWindows license
10/////////////////////////////////////////////////////////////////////////////
11
12// ----------------------------------------------------------------------------
13// headers
14// ----------------------------------------------------------------------------
15#ifdef __GNUG__
16 #pragma implementation "htmlparser.h"
17#endif
18
19#include "wx/wx.h"
20
21#ifdef __BORLANDC__
22#pragma hdrstop
23#endif
24
25#include "wx/textfile.h"
26#include "wx/wfstream.h"
27#include "wx/txtstrm.h"
28
29#include <ctype.h>
30
31#include "htmlparser.h"
32
33/// Useful insertion operators for wxOutputStream.
34static wxOutputStream& operator <<(wxOutputStream& stream, const wxString& s)
35{
36 wxTextOutputStream txt(stream); // This is to make sure the line-ending is native!
37
38 txt.WriteString(s);
39 return stream;
40}
41
42#if 0 // Gives warning because not used...
43static wxOutputStream& operator <<(wxOutputStream& stream, long l)
44{
45 wxString str;
46 str.Printf("%ld", l);
47 return stream << str;
48}
49
50static wxOutputStream& operator <<(wxOutputStream& stream, const char c)
51{
52 wxString str;
53 str.Printf("%c", c);
54 return stream << str;
55}
56#endif // 0
57
58/*
59 * wxSimpleHtmlAttribute
60 * Representation of an attribute
61 */
62
63wxSimpleHtmlParser::wxSimpleHtmlParser()
64{
65 m_topLevel = NULL;
66 m_pos = 0;
67}
68
69
70wxSimpleHtmlParser::~wxSimpleHtmlParser()
71{
72 Clear();
73}
74
75bool wxSimpleHtmlParser::ParseFile(const wxString& filename)
76{
77 wxTextFile textFile;
78
79 if (textFile.Open(filename))
80 {
81 wxString text;
82 wxString line;
83 int i;
84 int count = textFile.GetLineCount();
85 for (i = 0; i < count; i++)
86 {
87 if (i == 0)
88 line = textFile.GetFirstLine();
89 else
90 line = textFile.GetNextLine();
91
92 text += line;
93 if (i != (count - 1))
94 text += wxT("\n");
95 }
96
97#if 0
98 for ( line = textFile.GetFirstLine(); !textFile.Eof(); line = textFile.GetNextLine() )
99 {
100 text += line;
101 if (!textFile.Eof())
102 text += wxT("\n");
103 }
104#endif
105
106 return ParseString(text);
107 }
108 else
109 return FALSE;
110}
111
112bool wxSimpleHtmlParser::ParseString(const wxString& str)
113{
114 Clear();
115
116 m_pos = 0;
117 m_text = str;
118 m_length = str.Length();
119
120 m_topLevel = new wxSimpleHtmlTag(wxT("TOPLEVEL"), wxSimpleHtmlTag_TopLevel);
121
122 bool bResult = ParseHtml(m_topLevel);
123
124 wxASSERT(bResult); // Failed to parse the TAGs.
125 // Hint: Check if every open tag has a close tag!
126
127 return bResult;
128}
129
130// Main recursive parsing function
131bool wxSimpleHtmlParser::ParseHtml(wxSimpleHtmlTag* parent)
132{
133 if (!parent)
134 return FALSE;
135
136 while (!Eof())
137 {
138 EatWhitespace();
139 if (IsComment())
140 {
141 ParseComment();
142 }
143 else if (IsDirective())
144 {
145 wxSimpleHtmlTag* tag = ParseDirective();
146 if (tag)
147 parent->AppendTag(tag);
148 }
149 else if (IsXMLDeclaration())
150 {
151 wxSimpleHtmlTag* tag = ParseXMLDeclaration();
152 if (tag)
153 parent->AppendTag(tag);
154 }
155 else if (IsTagClose())
156 {
157 wxSimpleHtmlTag* tag = ParseTagClose();
158 if (tag)
159 {
160 if (IsCloseTagNeeded(tag->GetName()))
161 {
162 if (!parent->GetParent())
163 return FALSE;
164 parent->GetParent()->AppendTag(tag);
165 return TRUE;
166 }
167 else
168 parent->AppendTag(tag);
169 }
170 }
171 else if (IsTagStartBracket(GetChar(m_pos)))
172 {
173 wxSimpleHtmlTag* tag = ParseTagHeader();
174 if (tag)
175 parent->AppendTag(tag);
176
177 if (IsCloseTagNeeded(tag->GetName()))
178 {
179 if (!ParseHtml(tag))
180 return FALSE; // Something didn't go ok, so don't continue.
181 }
182 }
183 else
184 {
185 // Just a text string
186 wxString text;
187 ParseText(text);
188
189 wxSimpleHtmlTag* tag = new wxSimpleHtmlTag(wxT("TEXT"), wxSimpleHtmlTag_Text);
190 tag->SetText(text);
191 if(parent->GetParent())
192 parent->GetParent()->AppendTag(tag);
193 else
194 parent->AppendTag(tag); // When this occurs it is probably the
195 // empty lines at the end of the file...
196 }
197 }
198 return TRUE;
199}
200
201// Plain text, up until an angled bracket
202bool wxSimpleHtmlParser::ParseText(wxString& text)
203{
204 while (!Eof() && GetChar(m_pos) != wxT('<'))
205 {
206 text += GetChar(m_pos);
207 m_pos ++;
208 }
209 DecodeSpecialChars(text);
210 return TRUE;
211}
212
213wxSimpleHtmlTag* wxSimpleHtmlParser::ParseTagHeader()
214{
215 if (IsTagStartBracket(GetChar(m_pos)))
216 {
217 m_pos ++;
218 EatWhitespace();
219
220 wxString word;
221 ReadWord(word, TRUE);
222
223 EatWhitespace();
224
225 wxSimpleHtmlTag* tag = new wxSimpleHtmlTag(word, wxSimpleHtmlTag_Open);
226
227 ParseAttributes(tag);
228
229 EatWhitespace();
230
231 if (IsTagEndBracket(GetChar(m_pos)))
232 m_pos ++;
233
234 return tag;
235 }
236 else
237 return NULL;
238}
239
240wxSimpleHtmlTag* wxSimpleHtmlParser::ParseTagClose()
241{
242 Matches(wxT("</"), TRUE);
243
244 EatWhitespace();
245
246 wxString word;
247 ReadWord(word, TRUE);
248
249 EatWhitespace();
250 m_pos ++;
251
252 wxSimpleHtmlTag* tag = new wxSimpleHtmlTag(word, wxSimpleHtmlTag_Close);
253 return tag;
254}
255
256bool wxSimpleHtmlParser::ParseAttributes(wxSimpleHtmlTag* tag)
257{
258 // Parse attributes of a tag header until we reach >
259 while (!IsTagEndBracket(GetChar(m_pos)) && !Eof())
260 {
261 EatWhitespace();
262
263 wxString attrName, attrValue;
264
265 if (IsString())
266 {
267 ReadString(attrName, TRUE);
268 tag->AppendAttribute(attrName, wxEmptyString);
269 }
270 else if (IsNumeric(GetChar(m_pos)))
271 {
272 ReadNumber(attrName, TRUE);
273 tag->AppendAttribute(attrName, wxEmptyString);
274 }
275 else
276 {
277 // Try to read an attribute name/value pair, or at least a name
278 // without the value
279 ReadLiteral(attrName, TRUE);
280 EatWhitespace();
281
282 if (GetChar(m_pos) == wxT('='))
283 {
284 m_pos ++;
285 EatWhitespace();
286
287 if (IsString())
288 ReadString(attrValue, TRUE);
289 else if (!Eof() && !IsTagEndBracket(GetChar(m_pos)))
290 ReadLiteral(attrValue, TRUE);
291 }
292 if (!attrName.IsEmpty())
293 tag->AppendAttribute(attrName, attrValue);
294 }
295 }
296 return TRUE;
297}
298
299// e.g. <!DOCTYPE ....>
300wxSimpleHtmlTag* wxSimpleHtmlParser::ParseDirective()
301{
302 Matches(wxT("<!"), TRUE);
303
304 EatWhitespace();
305
306 wxString word;
307 ReadWord(word, TRUE);
308
309 EatWhitespace();
310
311 wxSimpleHtmlTag* tag = new wxSimpleHtmlTag(word, wxSimpleHtmlTag_Directive);
312
313 ParseAttributes(tag);
314
315 EatWhitespace();
316
317 if (IsTagEndBracket(GetChar(m_pos)))
318 m_pos ++;
319
320 return tag;
321}
322
323// e.g. <?xml .... ?>
324wxSimpleHtmlTag* wxSimpleHtmlParser::ParseXMLDeclaration()
325{
326 Matches(wxT("<?"), TRUE);
327
328 EatWhitespace();
329
330 wxString word;
331 ReadWord(word, TRUE);
332
333 EatWhitespace();
334
335 wxSimpleHtmlTag* tag = new wxSimpleHtmlTag(word, wxSimpleHtmlTag_XMLDeclaration);
336
337 ParseAttributes(tag);
338
339 EatWhitespace();
340
341 if (IsTagEndBracket(GetChar(m_pos)))
342 m_pos ++;
343
344 return tag;
345}
346
347bool wxSimpleHtmlParser::ParseComment()
348{
349 // Eat the comment tag start
350 Matches(wxT("<!--"), TRUE);
351
352 while (!Eof() && !Matches(wxT("-->"), TRUE))
353 {
354 m_pos ++;
355 }
356
357 return TRUE;
358}
359
360bool wxSimpleHtmlParser::EatWhitespace()
361{
362 while (!Eof() && IsWhitespace(GetChar(m_pos)))
363 m_pos ++;
364 return TRUE;
365}
366
367bool wxSimpleHtmlParser::EatWhitespace(int& pos)
368{
369 while (!Eof(pos) && IsWhitespace(GetChar(pos)))
370 pos ++;
371 return TRUE;
372}
373
374bool wxSimpleHtmlParser::ReadString(wxString& str, bool eatIt)
375{
376 int pos = m_pos;
377 if (GetChar(pos) == (int) '"')
378 {
379 pos ++;
380 while (!Eof(pos) && GetChar(pos) != (int) '"')
381 {
382 // TODO: how are quotes escaped in HTML?
383 str += (wxChar) GetChar(pos);
384 pos ++;
385 }
386 if (GetChar(pos) == (int) '"')
387 pos ++;
388 if (eatIt)
389 m_pos = pos;
390 DecodeSpecialChars(str);
391 return TRUE;
392 }
393 else
394 return FALSE;
395}
396
397bool wxSimpleHtmlParser::ReadWord(wxString& str, bool eatIt)
398{
399 int pos = m_pos;
400
401 if (!IsAlpha(GetChar(pos)))
402 return FALSE;
403
404 str += (wxChar) GetChar(pos) ;
405 pos ++;
406
407 while (!Eof(pos) && IsWordChar(GetChar(pos)))
408 {
409 str += (wxChar) GetChar(pos);
410 pos ++;
411 }
412 if (eatIt)
413 m_pos = pos;
414 DecodeSpecialChars(str);
415 return TRUE;
416}
417
418bool wxSimpleHtmlParser::ReadNumber(wxString& str, bool eatIt)
419{
420 int pos = m_pos;
421
422 if (!IsNumeric(GetChar(pos)))
423 return FALSE;
424
425 str += (wxChar) GetChar(pos) ;
426 pos ++;
427
428 while (!Eof(pos) && IsNumeric(GetChar(pos)))
429 {
430 str += (wxChar) GetChar(pos);
431 pos ++;
432 }
433 if (eatIt)
434 m_pos = pos;
435 DecodeSpecialChars(str);
436 return TRUE;
437}
438
439// Could be number, string, whatever, but read up until whitespace or end of tag (but not a quoted string)
440bool wxSimpleHtmlParser::ReadLiteral(wxString& str, bool eatIt)
441{
442 int pos = m_pos;
443
444 while (!Eof(pos) && !IsWhitespace(GetChar(pos)) && !IsTagEndBracket(GetChar(pos)) && GetChar(pos) != wxT('='))
445 {
446 str += GetChar(pos);
447 pos ++;
448 }
449 if (eatIt)
450 m_pos = pos;
451 DecodeSpecialChars(str);
452 return TRUE;
453}
454
455bool wxSimpleHtmlParser::IsComment()
456{
457 return Matches(wxT("<!--"));
458}
459
460bool wxSimpleHtmlParser::IsDirective()
461{
462 return Matches(wxT("<!"));
463}
464
465bool wxSimpleHtmlParser::IsXMLDeclaration()
466{
467 return Matches(wxT("<?xml"));
468}
469
470bool wxSimpleHtmlParser::IsString()
471{
472 return (GetChar(m_pos) == (int) '"') ;
473}
474
475bool wxSimpleHtmlParser::IsWord()
476{
477 return (IsAlpha(GetChar(m_pos)));
478}
479
480bool wxSimpleHtmlParser::IsTagClose()
481{
482 return Matches(wxT("</"));
483}
484
485bool wxSimpleHtmlParser::IsTagStartBracket(int ch)
486{
487 return (ch == wxT('<'));
488}
489
490bool wxSimpleHtmlParser::IsTagEndBracket(int ch)
491{
492 return (ch == wxT('>'));
493}
494
495bool wxSimpleHtmlParser::IsWhitespace(int ch)
496{
497 return ((ch == 13) || (ch == 10) || (ch == 32) || (ch == (int) '\t')) ;
498}
499
500bool wxSimpleHtmlParser::IsAlpha(int ch)
501{
502 return (wxIsalpha((wxChar) ch) != 0);
503}
504
505bool wxSimpleHtmlParser::IsWordChar(int ch)
506{
507 return (wxIsalpha((wxChar) ch) != 0 || ch == wxT('-') || ch == wxT('_') || IsNumeric(ch));
508}
509
510bool wxSimpleHtmlParser::IsNumeric(int ch)
511{
512 return (wxIsdigit((wxChar) ch) != 0 || ch == wxT('-') || ch == wxT('.')) ;
513}
514
515bool wxSimpleHtmlParser::IsCloseTagNeeded(const wxString &name)
516{
517 if (name.IsSameAs(wxT("P"), FALSE)) // e.g <P>
518 return FALSE;
519
520 // ToDo add more items here.
521
522 return TRUE;
523}
524
525// Encode/Decode Special Characters.
526// See here for the used table: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/xmlsql/ac_xml1_1nqk.asp
527/* static */ void wxSimpleHtmlParser::DecodeSpecialChars(wxString &value)
528{
529 // XML translation
530 value.Replace(wxT("&gt;"), wxT(">"), TRUE);
531 value.Replace(wxT("&lt;"), wxT("<"), TRUE);
532 value.Replace(wxT("&quot;"), wxT("\""), TRUE);
533 value.Replace(wxT("&apos;"), wxT("'"), TRUE);
534 value.Replace(wxT("&amp;"), wxT("&"), TRUE); // Note: do this as last to prevent replace problems.
535}
536
537/* static */ wxString wxSimpleHtmlParser::EncodeSpecialChars(const wxString &value)
538{
539 wxString newvalue = value;
540
541 // XML translation
542 newvalue.Replace(wxT("&"), wxT("&amp;"), TRUE); // Note: do this as first to prevent replace problems.
543 newvalue.Replace(wxT(">"), wxT("&gt;"), TRUE);
544 newvalue.Replace(wxT("<"), wxT("&lt;"), TRUE);
545 newvalue.Replace(wxT("\""),wxT("&quot;"), TRUE);
546 newvalue.Replace(wxT("'"), wxT("&apos;"), TRUE);
547
548 return newvalue;
549}
550
551// Matches this string (case insensitive)
552bool wxSimpleHtmlParser::Matches(const wxString& tok, bool eatIt)
553{
554 wxString text(m_text.Mid(m_pos, tok.Length()));
555 bool success = (text.CmpNoCase(tok) == 0) ;
556 if (success && eatIt)
557 {
558 m_pos += tok.Length();
559 }
560 return success;
561}
562
563// Safe way of getting a character
564int wxSimpleHtmlParser::GetChar(size_t i) const
565{
566 if (i >= (size_t) m_length)
567 return -1;
568 return m_text[i];
569}
570
571void wxSimpleHtmlParser::Clear()
572{
573 if (m_topLevel)
574 delete m_topLevel;
575 m_topLevel = NULL;
576 m_text = wxEmptyString;
577 m_pos = 0;
578 m_length = 0;
579}
580
581// Write this file
582void wxSimpleHtmlParser::Write(wxOutputStream& stream)
583{
584 if (m_topLevel)
585 m_topLevel->Write(stream);
586}
587
588bool wxSimpleHtmlParser::WriteFile(wxString& filename)
589{
590 wxFileOutputStream fstream(filename);
591 if (fstream.Ok())
592 {
593 Write(fstream);
594 return TRUE;
595 }
596 else
597 return FALSE;
598}
599
600/*
601 * wxSimpleHtmlTag
602 * Representation of a tag or chunk of text
603 */
604
605wxSimpleHtmlTag::wxSimpleHtmlTag(const wxString& tagName, int tagType)
606{
607 m_name = tagName;
608 m_type = tagType;
609 m_attributes = NULL;
610 m_children = NULL;
611 m_parent = NULL;
612 m_next = NULL;
613}
614
615wxSimpleHtmlTag::~wxSimpleHtmlTag()
616{
617 ClearAttributes();
618 ClearChildren();
619}
620
621//// Operations
622void wxSimpleHtmlTag::ClearAttributes()
623{
624 if (m_attributes)
625 {
626 wxSimpleHtmlAttribute* attr = m_attributes;
627 while (attr)
628 {
629 wxSimpleHtmlAttribute* next = attr->m_next;
630
631 attr->m_next = NULL;
632 delete attr;
633 attr = next;
634 }
635 m_attributes = NULL;
636 }
637}
638
639wxSimpleHtmlAttribute* wxSimpleHtmlTag::FindAttribute(const wxString& name) const
640{
641 wxSimpleHtmlAttribute* attr = m_attributes;
642 while (attr)
643 {
644 if (attr->GetName().CmpNoCase(name) == 0)
645 {
646 return attr;
647 }
648 attr = attr->m_next;
649 }
650 return NULL;
651}
652
653void wxSimpleHtmlTag::AppendAttribute(const wxString& name, const wxString& value)
654{
655 wxSimpleHtmlAttribute* attr = new wxSimpleHtmlAttribute(name, value);
656 if (m_attributes)
657 {
658 // Find tail
659 wxSimpleHtmlAttribute* last = m_attributes;
660 while (last->m_next)
661 last = last->m_next;
662
663 last->m_next = attr;
664 }
665 else
666 m_attributes = attr;
667}
668
669void wxSimpleHtmlTag::ClearChildren()
670{
671 if (m_children)
672 {
673 wxSimpleHtmlTag* child = m_children;
674 while (child)
675 {
676 wxSimpleHtmlTag* next = child->m_next;
677
678 child->m_next = NULL;
679 delete child;
680 child = next;
681 }
682 m_children = NULL;
683 }
684}
685
686void wxSimpleHtmlTag::RemoveChild(wxSimpleHtmlTag *remove)
687{
688 if (m_children)
689 {
690 wxSimpleHtmlTag* child = m_children;
691 wxSimpleHtmlTag* prev = NULL;
692 while (child)
693 {
694 wxSimpleHtmlTag* next = child->m_next;
695
696 if (child == remove)
697 {
698 child->m_next = NULL;
699 delete child;
700
701 if (prev != NULL)
702 prev->m_next = next;
703 else
704 m_children = next;
705
706 return;
707 }
708 prev = child;
709 child = next;
710 }
711 }
712}
713
714void wxSimpleHtmlTag::AppendTag(wxSimpleHtmlTag* tag)
715{
716 if (!tag)
717 return;
718
719 if (m_children)
720 {
721 // Find tail
722 wxSimpleHtmlTag* last = m_children;
723 while (last->m_next)
724 last = last->m_next;
725
726 last->m_next = tag;
727 }
728 else
729 {
730 m_children = tag;
731 }
732
733 tag->m_parent = this;
734}
735
736void wxSimpleHtmlTag::AppendTagAfterUs(wxSimpleHtmlTag* tag)
737{
738 if (!tag)
739 return;
740
741 tag->m_parent = m_parent;
742 tag->m_next = m_next;
743 m_next = tag;
744}
745
746// Gets the text from this tag and its descendants
747wxString wxSimpleHtmlTag::GetTagText()
748{
749 wxString text;
750 if (m_children)
751 {
752 wxSimpleHtmlTag* tag = m_children;
753 while (tag)
754 {
755 text += tag->GetTagText();
756 tag = tag->m_next;
757 }
758 return text;
759 }
760 else if (GetType() == wxSimpleHtmlTag_Text)
761 return GetText();
762 else
763 return wxEmptyString;
764}
765
766int wxSimpleHtmlTag::GetAttributeCount() const
767{
768 int count = 0;
769 wxSimpleHtmlAttribute* attr = m_attributes;
770 while (attr)
771 {
772 count ++;
773 attr = attr->m_next;
774 }
775 return count;
776}
777
778wxSimpleHtmlAttribute* wxSimpleHtmlTag::GetAttribute(int i) const
779{
780 int count = 0;
781 wxSimpleHtmlAttribute* attr = m_attributes;
782 while (attr)
783 {
784 if (count == i)
785 return attr;
786 count ++;
787 attr = attr->m_next;
788 }
789 return NULL;
790}
791
792int wxSimpleHtmlTag::GetChildCount() const
793{
794 int count = 0;
795 wxSimpleHtmlTag* tag = m_children;
796 while (tag)
797 {
798 count ++;
799 tag = tag->m_next;
800 }
801 return count;
802}
803
804bool wxSimpleHtmlTag::HasAttribute(const wxString& name, const wxString& value) const
805{
806 wxSimpleHtmlAttribute* attr = FindAttribute(name);
807
808 return (attr && (attr->GetValue().CmpNoCase(value) == 0)) ;
809}
810
811bool wxSimpleHtmlTag::HasAttribute(const wxString& name) const
812{
813 return FindAttribute(name) != NULL ;
814}
815
816bool wxSimpleHtmlTag::GetAttributeValue(wxString& value, const wxString& attrName)
817{
818 wxSimpleHtmlAttribute* attr = FindAttribute(attrName);
819 if (attr)
820 {
821 value = attr->GetValue();
822 return TRUE;
823 }
824 else
825 return FALSE;
826}
827
828// Search forward from this tag until we find a tag with this name & attribute
829wxSimpleHtmlTag* wxSimpleHtmlTag::FindTag(const wxString& tagName, const wxString& attrName)
830{
831 wxSimpleHtmlTag* tag = m_next;
832 while (tag)
833 {
834 if (tag->NameIs(tagName) && (attrName.IsEmpty() || tag->FindAttribute(attrName)))
835 return tag;
836
837 tag = tag->m_next;
838 }
839 return NULL;
840}
841
842bool wxSimpleHtmlTag::FindTextUntilTagClose(wxString& text, const wxString& tagName)
843{
844 wxSimpleHtmlTag* tag = this;
845 while (tag)
846 {
847 if (tag->GetType() == wxSimpleHtmlTag_Close && tag->NameIs(tagName))
848 return TRUE;
849
850 if (tag->GetType() == wxSimpleHtmlTag_Text)
851 text += tag->GetText();
852
853 tag = tag->m_next;
854 }
855 return TRUE;
856}
857
858
859wxSimpleHtmlTag* wxSimpleHtmlTag::GetChild(int i) const
860{
861 int count = 0;
862 wxSimpleHtmlTag* tag = m_children;
863 while (tag)
864 {
865 if (count == i)
866 return tag;
867
868 count ++;
869 tag = tag->m_next;
870 }
871 return NULL;
872}
873
874void wxSimpleHtmlTag::Write(wxOutputStream& stream)
875{
876 // Some helpers to layout the open and close tags.
877 static bool sbUseTab = TRUE;
878 static size_t snTabLevel = 0;
879
880#if 0 // Enable if no tabs should be used to align the tags.
881 snTabLevel = 0;
882#endif
883
884 // Handle the different types of tags we can write.
885 switch (GetType())
886 {
887 case wxSimpleHtmlTag_Text:
888 {
889 stream << wxSimpleHtmlParser::EncodeSpecialChars(m_text);
890 break;
891 }
892 case wxSimpleHtmlTag_Open:
893 {
894 size_t tab;
895 for(tab = 0; tab < snTabLevel; tab++)
896 stream << wxT("\t");
897 stream << wxT("<") << wxSimpleHtmlParser::EncodeSpecialChars(m_name);
898 if (GetAttributeCount() > 0)
899 stream << wxT(" ");
900 int i;
901 for (i = 0; i < GetAttributeCount(); i++)
902 {
903 wxSimpleHtmlAttribute* attr = GetAttribute(i);
904 attr->Write(stream);
905 if (i < GetAttributeCount() - 1)
906 stream << wxT(" ");
907 }
908 if(!m_children)
909 {
910 sbUseTab = FALSE; // We're putting the open a close tag on the same line,
911 // so we don't wan't any tabs
912 stream << wxT(">");
913 }
914 else
915 {
916 // sbUseTab = TRUE;
917 stream << wxT(">\n");
918 }
919 snTabLevel++;
920 break;
921 }
922 case wxSimpleHtmlTag_Directive:
923 {
924 stream << wxT("<!") << wxSimpleHtmlParser::EncodeSpecialChars(m_name) << wxT(" ");
925 int i;
926 for (i = 0; i < GetAttributeCount(); i++)
927 {
928 wxSimpleHtmlAttribute* attr = GetAttribute(i);
929 attr->Write(stream);
930 if (i < GetAttributeCount() - 1)
931 stream << wxT(" ");
932 }
933 stream << wxT(">\n");
934 break;
935 }
936 case wxSimpleHtmlTag_XMLDeclaration:
937 {
938 stream << wxT("<?") << wxSimpleHtmlParser::EncodeSpecialChars(m_name) << wxT(" ");
939 int i;
940 for (i = 0; i < GetAttributeCount(); i++)
941 {
942 wxSimpleHtmlAttribute* attr = GetAttribute(i);
943 attr->Write(stream);
944 if (i < GetAttributeCount() - 1)
945 stream << wxT(" ");
946 }
947 stream << wxT(">\n\n");
948 break;
949 }
950 case wxSimpleHtmlTag_Close:
951 {
952 if (snTabLevel) // Safety to prevent going around...
953 snTabLevel--; // Reduce the tab level
954 if (sbUseTab) // Do we write the open tag and close tag on a other line?
955 {
956 size_t tab;
957 for(tab = 0; tab < snTabLevel; tab++)
958 stream << wxT("\t");
959 }
960 stream << wxT("</") << wxSimpleHtmlParser::EncodeSpecialChars(m_name) << wxT(">\n");
961 sbUseTab = TRUE;
962 break;
963 }
964 default:
965 {
966 break;
967 }
968 }
969 wxSimpleHtmlTag* tag = m_children;
970 while (tag)
971 {
972 tag->Write(stream);
973 tag = tag->m_next;
974 }
975
976}
977
978void wxSimpleHtmlAttribute::Write(wxOutputStream& stream)
979{
980 if (m_value.IsEmpty())
981 stream << wxSimpleHtmlParser::EncodeSpecialChars(m_name);
982 else
983 {
984 stream << wxSimpleHtmlParser::EncodeSpecialChars(m_name);
985 stream << wxT("=\"");
986 stream << wxSimpleHtmlParser::EncodeSpecialChars(m_value);
987 stream << wxT("\"");
988 }
989}