PLaSK library

Loading...
Searching...
No Matches
writer.cpp
Go to the documentation of this file.
1/*
2 * This file is part of PLaSK (https://plask.app) by Photonics Group at TUL
3 * Copyright (c) 2022 Lodz University of Technology
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, version 3.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 */
14#include "writer.hpp"
15
16#include "exceptions.hpp"
17
18#include <fstream>
19
20 namespace plask {
21
22 template <typename ostream_t>
23 struct OStreamOutput: public XMLWriter::Output {
24
25 ostream_t ostream;
26
27 OStreamOutput(ostream_t& stream_ref): ostream(stream_ref) {
28 }
29
30 template <typename... StreamCtorArgT>
31 OStreamOutput(StreamCtorArgT&&... stream_open_arg) {
32 ostream.exceptions(std::ifstream::failbit | std::ifstream::badbit);
33 ostream.open(std::forward<StreamCtorArgT>(stream_open_arg)...);
34 }
35
36 void write(const char *buffer, std::size_t n) override {
37 ostream.write(buffer, n);
38 }
39
40 void put(char c) override {
41 ostream.put(c);
42 }
43
44};
45
46 typedef OStreamOutput<std::ostream&> OStreamRef;
47 typedef OStreamOutput<std::ofstream> OFStream;
48
49 struct CFileOutput: public XMLWriter::Output {
50
51 FILE* file;
52
53 CFileOutput(FILE* file): file(file) {}
54
55 void write(const char *buffer, std::size_t n) override {
56 if (std::fwrite(buffer, 1, n, file) != n)
57 throw XMLWriterException("XML writer can't write to C file descriptor.");
58 }
59
60 /*void put(char c) {
61 std::fputc(c, file);
62 }*/
63
64};
65
66 XMLWriter::Element::Element(XMLWriter &writer, const std::string &name)
67: name(name), writer(&writer), hasChildren(false) {
68 writeOpening();
69}
70
71 XMLWriter::Element::Element(XMLWriter &writer, std::string &&name)
72: name(std::move(name)), writer(&writer), hasChildren(false) {
73 writeOpening();
74}
75
76 XMLWriter::Element::Element(XMLWriter::Element &parent, const std::string &name)
77: name(name), writer(parent.writer), hasChildren(false) {
78 parent.ensureIsCurrent();
79 writeOpening();
80}
81
82 XMLWriter::Element::Element(XMLWriter::Element &parent, std::string&& name)
83: name(std::move(name)), writer(parent.writer), hasChildren(false) {
84 parent.ensureIsCurrent();
85 writeOpening();
86}
87
88 XMLWriter::Element::Element(XMLWriter::Element&& to_move) {
89 this->operator =(std::move(to_move));
90/* to_move.ensureIsCurrent();
91 name = std::move(to_move.name);
92 writer = to_move.writer;
93 to_move.writer = 0;
94 parent = to_move.parent;
95 attributesStillAlowed = to_move.attributesStillAlowed;
96 this->writer->current = this;*/
97}
98
99 XMLWriter::Element &XMLWriter::Element::operator=(XMLWriter::Element && to_move) {
100 to_move.ensureIsCurrent();
101 name = std::move(to_move.name);
102 writer = to_move.writer;
103 parent = to_move.parent;
104 attributesStillAlowed = to_move.attributesStillAlowed;
105 hasChildren = to_move.hasChildren;
106 to_move.writer = 0;
107 this->writer->current = this;
108 return *this;
109}
110
111 XMLWriter::Element::~Element() {
112 if (!writer) return; // element already moved
113 writeClosing();
114}
115
116std::size_t XMLWriter::Element::getLevel() const {
117 std::size_t result = 0;
118 for (Element* i = this->parent; i != 0; i = i->parent) ++result;
119 return result;
120}
121
122 XMLWriter::Element &XMLWriter::Element::attr(const std::string &attr_name, const std::string &attr_value) {
123 if (!attributesStillAlowed)
124 throw XMLWriterException(format("can't append attribute \"{1}\" to \"{0}\" XML element because this element has already non-empty content.", name, attr_name));
125 writer->out->put(' ');
126 writer->appendStr(attr_name);
127 writer->out->puts("=\"");
128 writer->appendStrQuoted(attr_value);
129 writer->out->put('"');
130 return *this;
131}
132
133 XMLWriter::Element &XMLWriter::Element::writeText(const char *str) {
134 ensureIsCurrent();
135 disallowAttributes();
136 writer->appendStrQuoted(str);
137 return *this;
138}
139
140 XMLWriter::Element &XMLWriter::Element::writeCDATA(const std::string& str) {
141 ensureIsCurrent();
142 disallowAttributes();
143 writer->out->puts("<![CDATA[");
144 writer->appendStr(str);
145 writer->out->puts("]]>");
146 return *this;
147}
148
149 void XMLWriter::Element::indent () {
150 if (disallowAttributes()) writer->out->newline();
151 std::size_t l = (getLevel() + 1) * writer->indentation;
152 while (l > 0) { writer->out->put(' '); --l; }
153}
154
155 XMLWriter::Element &XMLWriter::Element::end() {
156 ensureIsCurrent();
157 writeClosing();
158 Element* current = writer->current; //new current tag, parent of this
159 this->writer = 0; // to not close a tag by destructor
160 return current ? *current : *this;
161}
162
163 void XMLWriter::Element::writeOpening() {
164 attributesStillAlowed = true;
165 parent = writer->current;
166 if (writer->current) {
167 writer->current->hasChildren = true;
168 if (writer->current->disallowAttributes()) //parent has nothing inside?
169 writer->out->newline();
170 }
171 writer->current = this;
172 std::size_t l = getLevel() * writer->indentation;
173 while (l > 0) { writer->out->put(' '); --l; }
174 writer->out->put('<');
175 writer->appendStr(name);
176}
177
178 void XMLWriter::Element::writeClosing()
179{
180 if (attributesStillAlowed) { //empty tag?
181 writer->out->puts("/>");
182 } else {
183 if (hasChildren) {
184 std::size_t l = getLevel() * writer->indentation;
185 while (l > 0) { writer->out->put(' '); --l; }
186 }
187 writer->out->puts("</");
188 writer->appendStr(name);
189 writer->out->put('>');
190 }
191 writer->out->newline(); //TODO ??
192 writer->current = this->parent;
193}
194
195 bool XMLWriter::Element::disallowAttributes() {
196 if (attributesStillAlowed) {
197 writer->out->put('>');
198 //writer->out->newline(); //TODO ??
199 writer->current->attributesStillAlowed = false;
200 return true;
201 } else
202 return false;
203}
204
205 void XMLWriter::Element::ensureIsCurrent() {
206 if (this != writer->current)
207 throw XMLWriterException("operation is not permitted as the XML element \""+ name +"\" is not the last one in the stack");
208}
209
210 XMLWriter::XMLWriter(std::ostream& out, std::size_t indentation)
211 : out(new OStreamRef(out)), current(0), indentation(indentation) {}
212
213 XMLWriter::XMLWriter(const std::string& file_name, std::size_t indentation)
214 : out(new OFStream(file_name.c_str())), current(0), indentation(indentation)
215{}
216
217 XMLWriter::XMLWriter(std::FILE* cfile, std::size_t indentation)
218 : out(new CFileOutput(cfile)), current(0), indentation(indentation)
219{}
220
221 XMLWriter::XMLWriter(Output* out, std::size_t indentation)
222 : out(out), current(0), indentation(indentation)
223{}
224
225 void XMLWriter::appendStrQuoted(const char *s) {
226 for (; *s; ++s)
227 switch (*s) {
228 case '"': out->puts("&quot;"); break;
229 case '<': out->puts("&lt;"); break;
230 case '>': out->puts("&gt;"); break;
231 case '&': out->puts("&amp;"); break;
232 case '\'': out->puts("&apos;"); break;
233 default: out->put(*s); break;
234 }
235}
236
237} // namespace plask