source: branches/help/XML/xml.cxx

Last change on this file was 18798, checked in by westram, 3 years ago
  • assert XML_Tag is not opened yet, when add_attribute() is called.
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 7.1 KB
Line 
1// --------------------------------------------------------------------------------
2//
3// Copyright (C) 2000
4// Ralf Westram
5//
6// Permission to use, copy, modify, distribute and sell this software
7// and its documentation for any purpose is hereby granted without fee,
8// provided that the above copyright notice appear in all copies and
9// that both that copyright notice and this permission notice appear
10// in supporting documentation.  Ralf Westram makes no
11// representations about the suitability of this software for any
12// purpose.  It is provided "as is" without express or implied warranty.
13//
14// This code is part of my library.
15// You may find a more recent version at http://www.reallysoft.de/
16//
17// --------------------------------------------------------------------------------
18
19#include "xml.hxx"
20
21using namespace std;
22
23XML_Document *the_XML_Document = NULp;
24
25static const char *entities =
26"  <!ENTITY nbsp \"&#160;\">\n"
27"  <!ENTITY acute \"&#180;\">\n" // Acute accent (forward)
28"  <!ENTITY eacute \"&#233;\">\n" // e Acute (forward)
29"  <!ENTITY apostr \"&#39;\">\n" // single quote (vertical)
30"  <!ENTITY semi \"&#59;\">\n"
31;
32
33static string encodeEntities(const string& str, bool /*quotedText*/) {
34    // if quotedText is true the string is encoded for usage in quotes
35    // currently it makes no difference, but this might change
36
37    string neu;
38    neu.reserve(str.length()*4);
39
40
41    for (string::const_iterator s = str.begin(); s != str.end(); ++s) {
42        char        replace = 0;
43        const char *entity  = NULp;
44
45        switch (*s) {
46            case '<':  { entity = "lt"; break; }
47            case '>':  { entity = "gt"; break; }
48            case '&':  { entity = "amp"; break; }
49            case '\'':  { entity = "apostr"; break; }
50            case char(0xb4):  { entity = "acute"; break; } // acute (forward)
51            case 'é': { entity = "eacute"; break; }
52            default:   { replace = *s; }
53            }
54
55        if (replace) {
56            neu.append(1, replace);
57        }
58        else {
59            xml_assert(entity);
60            neu.append(1, '&');
61            neu.append(entity);
62            neu.append(1, ';');
63        }
64    }
65
66    return neu;
67}
68
69// ----------------------
70//      XML_Attribute
71
72XML_Attribute::XML_Attribute(const string& name_, const string& content_) :
73    name(name_),
74    content(content_),
75    next(NULp)
76{}
77
78XML_Attribute::~XML_Attribute() {
79    delete next;
80}
81XML_Attribute *XML_Attribute::append_to(XML_Attribute *queue) {
82    if (!queue) return this;
83    queue->next = append_to(queue->next);
84    return queue;
85}
86void XML_Attribute::print(FILE *out) const {
87    fprintf(out, " %s=\"%s\"", name.c_str(), encodeEntities(content, true).c_str());
88    if (next) next->print(out);
89}
90
91// -----------------
92//      XML_Node
93
94XML_Node::XML_Node(bool is_tag) {
95    xml_assert(the_XML_Document);
96
97    father = the_XML_Document->LatestSon();
98    the_XML_Document->set_LatestSon(this);
99    indent = 0;
100
101    if (father) {
102        father->add_son(this, is_tag);
103        indent = father->Indent()+1;
104    }
105
106    opened = false;
107}
108
109XML_Node::~XML_Node() {
110    if (father) father->remove_son(this);
111    the_XML_Document->set_LatestSon(father);
112}
113
114
115// ----------------
116//      XML_Tag
117
118XML_Tag::XML_Tag(const string &name_) :
119    XML_Node(true),
120    name(name_),
121    son(NULp),
122    attribute(NULp),
123    state(0),
124    onExtraLine(true)
125{}
126
127XML_Tag::~XML_Tag() {
128    FILE *out = the_XML_Document->Out();
129    xml_assert(!son);
130    close(out);
131
132    delete attribute;
133}
134
135void XML_Tag::add_attribute(const string& name_, const string& content_) {
136    xml_assert(!Opened());
137    XML_Attribute *newAttr = new XML_Attribute(name_, content_);
138    attribute              = newAttr->append_to(attribute);
139}
140
141void XML_Tag::add_attribute(const string& name_, int value) {
142    char buf[30];
143    sprintf(buf, "%i", value);
144    add_attribute(name_, buf);
145}
146
147void XML_Tag::add_son(XML_Node *son_, bool son_is_tag) {
148    if (son) throw string("Tried to add a second son! Destroy previous son first.");
149    son                           = son_;
150    int wanted_state              = son_is_tag ? 2 : 1;
151    if (state<wanted_state) state = wanted_state;
152}
153
154void XML_Tag::remove_son(XML_Node *son_) {
155    if (son != son_) throw string("Tried to remove wrong son!");
156    son = NULp;
157}
158
159inline void to_indent(FILE *out, int indent) { int i = indent*the_XML_Document->indentation_per_level; while (i--) fputc(' ', out); }
160
161void XML_Tag::open(FILE *out) {
162    if (father && !father->Opened()) father->open(out);
163    if (onExtraLine) {
164        fputc('\n', out);
165        to_indent(out, Indent());
166    }
167    fputc('<', out); fputs(name.c_str(), out);
168    if (attribute) attribute->print(out);
169    fputc('>', out);
170    opened = true;
171}
172
173void XML_Tag::close(FILE *out) {
174    if (!opened) {
175        if (!the_XML_Document->skip_empty_tags || attribute || !father) {
176            if (father && !father->Opened()) father->open(out);
177            if (onExtraLine) {
178                fputc('\n', out);
179                to_indent(out, Indent());
180            }
181            fputc('<', out); fputs(name.c_str(), out);
182            if (attribute) attribute->print(out);
183            fputs("/>", out);
184        }
185    }
186    else {
187        if (state >= 2 && onExtraLine) { fputc('\n', out); to_indent(out, Indent()); }
188        fprintf(out, "</%s>", name.c_str());
189    }
190}
191
192// -----------------
193//      XML_Text
194
195XML_Text::~XML_Text() {
196    FILE *out = the_XML_Document->Out();
197    close(out);
198}
199
200void XML_Text::add_son(XML_Node *, bool) {
201    throw string("Can't add son to XML_Text-Node");
202}
203
204void XML_Text::remove_son(XML_Node * /* son_ */) {
205    throw string("Can't remove son from XML_Text-Node");
206}
207
208void XML_Text::open(FILE *) {}
209
210void XML_Text::close(FILE *out) {
211    if (father && !father->Opened()) father->open(out);
212    fputs(encodeEntities(content, false).c_str(), out);
213}
214
215// --------------------
216//      XML_Comment
217
218XML_Comment::~XML_Comment() {
219    FILE *out = the_XML_Document->Out();
220    close(out);
221}
222
223void XML_Comment::add_son(XML_Node *, bool) {
224    throw string("Can't add son to XML_Comment-Node");
225}
226
227void XML_Comment::remove_son(XML_Node *) {
228    throw string("Can't remove son from XML_Comment-Node");
229}
230
231void XML_Comment::open(FILE *) {}
232
233void XML_Comment::close(FILE *out) {
234    fputc('\n', out); to_indent(out, Indent());
235    fputs("<!--", out);
236    fputs(encodeEntities(content, false).c_str(), out);
237    fputs("-->", out);
238}
239
240// ---------------------
241//      XML_Document
242
243XML_Document::XML_Document(const string& name_, const string& dtd_, FILE *out_)
244    : dtd(dtd_),
245      root(NULp),
246      out(out_),
247      skip_empty_tags(false),
248      indentation_per_level(1)
249{
250    xml_assert(out);
251    if (the_XML_Document) GBK_terminate("You can only have one XML_Document at a time.");
252    the_XML_Document = this;
253    latest_son       = NULp;
254    root             = new XML_Tag(name_);
255    xml_assert(latest_son == root);
256
257    fputs("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n", out);
258    fprintf(out, "<!DOCTYPE %s SYSTEM '%s' [\n%s]>\n", name_.c_str(), dtd.c_str(), entities);
259}
260
261XML_Document::~XML_Document() {
262    delete root;
263    xml_assert(the_XML_Document == this);
264    the_XML_Document = NULp;
265}
266
267
Note: See TracBrowser for help on using the repository browser.