source: branches/items/XML/xml.cxx

Last change on this file was 16763, checked in by westram, 7 years ago
  • 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_Attribute *newAttr = new XML_Attribute(name_, content_);
137    attribute = newAttr->append_to(attribute);
138}
139
140void XML_Tag::add_attribute(const string& name_, int value) {
141    char buf[30];
142    sprintf(buf, "%i", value);
143    add_attribute(name_, buf);
144}
145
146void XML_Tag::add_son(XML_Node *son_, bool son_is_tag) {
147    if (son) throw string("Tried to add a second son! Destroy previous son first.");
148    son                           = son_;
149    int wanted_state              = son_is_tag ? 2 : 1;
150    if (state<wanted_state) state = wanted_state;
151}
152
153void XML_Tag::remove_son(XML_Node *son_) {
154    if (son != son_) throw string("Tried to remove wrong son!");
155    son = NULp;
156}
157
158inline void to_indent(FILE *out, int indent) { int i = indent*the_XML_Document->indentation_per_level; while (i--) fputc(' ', out); }
159
160void XML_Tag::open(FILE *out) {
161    if (father && !father->Opened()) father->open(out);
162    if (onExtraLine) {
163        fputc('\n', out);
164        to_indent(out, Indent());
165    }
166    fputc('<', out); fputs(name.c_str(), out);
167    if (attribute) attribute->print(out);
168    fputc('>', out);
169    opened = true;
170}
171
172void XML_Tag::close(FILE *out) {
173    if (!opened) {
174        if (!the_XML_Document->skip_empty_tags || attribute || !father) {
175            if (father && !father->Opened()) father->open(out);
176            if (onExtraLine) {
177                fputc('\n', out);
178                to_indent(out, Indent());
179            }
180            fputc('<', out); fputs(name.c_str(), out);
181            if (attribute) attribute->print(out);
182            fputs("/>", out);
183        }
184    }
185    else {
186        if (state >= 2 && onExtraLine) { fputc('\n', out); to_indent(out, Indent()); }
187        fprintf(out, "</%s>", name.c_str());
188    }
189}
190
191// -----------------
192//      XML_Text
193
194XML_Text::~XML_Text() {
195    FILE *out = the_XML_Document->Out();
196    close(out);
197}
198
199void XML_Text::add_son(XML_Node *, bool) {
200    throw string("Can't add son to XML_Text-Node");
201}
202
203void XML_Text::remove_son(XML_Node * /* son_ */) {
204    throw string("Can't remove son from XML_Text-Node");
205}
206
207void XML_Text::open(FILE *) {}
208
209void XML_Text::close(FILE *out) {
210    if (father && !father->Opened()) father->open(out);
211    fputs(encodeEntities(content, false).c_str(), out);
212}
213
214// --------------------
215//      XML_Comment
216
217XML_Comment::~XML_Comment() {
218    FILE *out = the_XML_Document->Out();
219    close(out);
220}
221
222void XML_Comment::add_son(XML_Node *, bool) {
223    throw string("Can't add son to XML_Comment-Node");
224}
225
226void XML_Comment::remove_son(XML_Node *) {
227    throw string("Can't remove son from XML_Comment-Node");
228}
229
230void XML_Comment::open(FILE *) {}
231
232void XML_Comment::close(FILE *out) {
233    fputc('\n', out); to_indent(out, Indent());
234    fputs("<!--", out);
235    fputs(encodeEntities(content, false).c_str(), out);
236    fputs("-->", out);
237}
238
239// ---------------------
240//      XML_Document
241
242XML_Document::XML_Document(const string& name_, const string& dtd_, FILE *out_)
243    : dtd(dtd_),
244      root(NULp),
245      out(out_),
246      skip_empty_tags(false),
247      indentation_per_level(1)
248{
249    xml_assert(out);
250    if (the_XML_Document) GBK_terminate("You can only have one XML_Document at a time.");
251    the_XML_Document = this;
252    latest_son       = NULp;
253    root             = new XML_Tag(name_);
254    xml_assert(latest_son == root);
255
256    fputs("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n", out);
257    fprintf(out, "<!DOCTYPE %s SYSTEM '%s' [\n%s]>\n", name_.c_str(), dtd.c_str(), entities);
258}
259
260XML_Document::~XML_Document() {
261    delete root;
262    xml_assert(the_XML_Document == this);
263    the_XML_Document = NULp;
264}
265
266
Note: See TracBrowser for help on using the repository browser.