1//
2// CodeWriter.cpp
3//
4// Copyright (c) 2008, Applied Informatics Software Engineering GmbH.
5// and Contributors.
6//
7// SPDX-License-Identifier: BSL-1.0
8//
9
10
11#include "CodeWriter.h"
12#include "Page.h"
13#include "Poco/Path.h"
14#include "Poco/StringTokenizer.h"
15#include "Poco/String.h"
16
17
18using Poco::Path;
19using Poco::StringTokenizer;
20
21
22CodeWriter::CodeWriter(const Page& page, const std::string& clazz):
23 _page(page),
24 _class(clazz)
25{
26}
27
28
29CodeWriter::~CodeWriter()
30{
31}
32
33
34void CodeWriter::writeHeader(std::ostream& ostr, const std::string& headerFileName)
35{
36 beginGuard(ostr, headerFileName);
37 writeHeaderIncludes(ostr);
38 ostr << "\n\n";
39
40 std::string decls(_page.headerDecls().str());
41 if (!decls.empty())
42 {
43 ostr << decls << "\n\n";
44 }
45
46 beginNamespace(ostr);
47 writeHandlerClass(ostr);
48 writeFactoryClass(ostr);
49 endNamespace(ostr);
50 endGuard(ostr, headerFileName);
51}
52
53
54void CodeWriter::writeImpl(std::ostream& ostr, const std::string& headerFileName)
55{
56 ostr << "#include \"" << headerFileName << "\"\n";
57 writeImplIncludes(ostr);
58 if (_page.getBool("page.compressed", false))
59 {
60 ostr << "#include \"Poco/DeflatingStream.h\"\n";
61 }
62 if (_page.getBool("page.buffered", false))
63 {
64 ostr << "#include \"Poco/StreamCopier.h\"\n";
65 ostr << "#include <sstream>\n";
66 }
67 ostr << "\n\n";
68
69 std::string decls(_page.implDecls().str());
70 if (!decls.empty())
71 {
72 ostr << decls << "\n\n";
73 }
74
75 beginNamespace(ostr);
76
77 std::string path = _page.get("page.path", "");
78 if (!path.empty())
79 {
80 ostr << "\tconst std::string " << _class << "::PATH(\"" << path << "\");\n\n\n";
81 }
82
83 writeConstructor(ostr);
84 writeHandler(ostr);
85 writeFactory(ostr);
86 endNamespace(ostr);
87 writeManifest(ostr);
88}
89
90
91void CodeWriter::beginNamespace(std::ostream& ostr)
92{
93 std::string ns = _page.get("page.namespace", "");
94 if (!ns.empty())
95 {
96 StringTokenizer tok(ns, ":", StringTokenizer::TOK_IGNORE_EMPTY | StringTokenizer::TOK_TRIM);
97 for (StringTokenizer::Iterator it = tok.begin(); it != tok.end(); ++it)
98 {
99 ostr << "namespace " << *it << " {\n";
100 }
101 ostr << "\n\n";
102 }
103}
104
105void CodeWriter::endNamespace(std::ostream& ostr)
106{
107 std::string ns = _page.get("page.namespace", "");
108 if (!ns.empty())
109 {
110 ostr << "\n\n";
111 StringTokenizer tok(ns, ":", StringTokenizer::TOK_IGNORE_EMPTY | StringTokenizer::TOK_TRIM);
112 for (StringTokenizer::Iterator it = tok.begin(); it != tok.end(); ++it)
113 {
114 ostr << "} ";
115 }
116 ostr << "// namespace " << ns << "\n";
117 }
118}
119
120
121void CodeWriter::beginGuard(std::ostream& ostr, const std::string& headerFileName)
122{
123 Path p(headerFileName);
124 std::string guard(p.getBaseName());
125 Poco::translateInPlace(guard, ".-", "__");
126 guard += "_INCLUDED";
127
128 ostr << "#ifndef " << guard << "\n";
129 ostr << "#define " << guard << "\n";
130 ostr << "\n\n";
131}
132
133
134void CodeWriter::endGuard(std::ostream& ostr, const std::string& headerFileName)
135{
136 Path p(headerFileName);
137 std::string guard(p.getBaseName());
138 Poco::translateInPlace(guard, ".-", "__");
139 guard += "_INCLUDED";
140 ostr << "\n\n";
141 ostr << "#endif // " << guard << "\n";
142}
143
144
145void CodeWriter::handlerClass(std::ostream& ostr, const std::string& base, const std::string& ctorArg)
146{
147 std::string exprt(_page.get("page.export", ""));
148 if (!exprt.empty()) exprt += ' ';
149
150 ostr << "class " << exprt << _class << ": public " << base << "\n";
151 ostr << "{\n";
152 ostr << "public:\n";
153 if (!ctorArg.empty())
154 {
155 ostr << "\t" << _class << "(" << ctorArg << ");\n";
156 ostr << "\n";
157 }
158 ostr << "\tvoid handleRequest(Poco::Net::HTTPServerRequest& request, Poco::Net::HTTPServerResponse& response);\n";
159 writeHandlerMembers(ostr);
160
161 std::string path = _page.get("page.path", "");
162 if (!path.empty())
163 {
164 ostr << "\n\tstatic const std::string PATH;\n";
165 }
166
167 ostr << "};\n";
168}
169
170
171void CodeWriter::factoryClass(std::ostream& ostr, const std::string& base)
172{
173 ostr << "class " << _class << "Factory: public " << base << "\n";
174 ostr << "{\n";
175 ostr << "public:\n";
176 ostr << "\tPoco::Net::HTTPRequestHandler* createRequestHandler(const Poco::Net::HTTPServerRequest& request);\n";
177 ostr << "};\n";
178}
179
180
181void CodeWriter::factoryImpl(std::ostream& ostr, const std::string& arg)
182{
183 ostr << "Poco::Net::HTTPRequestHandler* " << _class << "Factory::createRequestHandler(const Poco::Net::HTTPServerRequest& request)\n";
184 ostr << "{\n";
185 ostr << "\treturn new " << _class << "(" << arg << ");\n";
186 ostr << "}\n";
187}
188
189
190void CodeWriter::writeHeaderIncludes(std::ostream& ostr)
191{
192 ostr << "#include \"Poco/Net/HTTPRequestHandler.h\"\n";
193}
194
195
196void CodeWriter::writeHandlerClass(std::ostream& ostr)
197{
198 std::string base(_page.get("page.baseClass", "Poco::Net::HTTPRequestHandler"));
199 std::string ctorArg;
200 ctorArg = _page.get("page.context", _page.get("page.ctorArg", ""));
201
202 handlerClass(ostr, base, ctorArg);
203}
204
205
206void CodeWriter::writeHandlerMembers(std::ostream& ostr)
207{
208 std::string context(_page.get("page.context", ""));
209 if (!context.empty())
210 {
211 ostr << "\n";
212 ostr << "protected:\n";
213 ostr << "\t" << context << " context() const\n";
214 ostr << "\t{\n";
215 ostr << "\t\treturn _context;\n";
216 ostr << "\t}\n";
217 ostr << "\n";
218 ostr << "private:\n";
219 ostr << "\t" << context << " _context;\n";
220 }
221}
222
223
224void CodeWriter::writeFactoryClass(std::ostream& ostr)
225{
226}
227
228
229void CodeWriter::writeImplIncludes(std::ostream& ostr)
230{
231 ostr << "#include \"Poco/Net/HTTPServerRequest.h\"\n";
232 ostr << "#include \"Poco/Net/HTTPServerResponse.h\"\n";
233 ostr << "#include \"Poco/Net/HTMLForm.h\"\n";
234}
235
236
237void CodeWriter::writeConstructor(std::ostream& ostr)
238{
239 std::string base(_page.get("page.baseClass", "Poco::Net::HTTPRequestHandler"));
240 std::string context(_page.get("page.context", ""));
241 std::string ctorArg(_page.get("page.ctorArg", ""));
242 if (!context.empty())
243 {
244 ostr << _class << "::" << _class << "(" << context << " context):\n";
245 ostr << "\t_context(context)\n";
246 ostr << "{\n}\n";
247 ostr << "\n\n";
248 }
249 else if (!ctorArg.empty())
250 {
251 ostr << _class << "::" << _class << "(" << ctorArg << " arg):\n";
252 ostr << "\t" << base << "(arg)\n";
253 ostr << "{\n}\n";
254 ostr << "\n\n";
255 }
256}
257
258
259void CodeWriter::writeHandler(std::ostream& ostr)
260{
261 ostr << "void " << _class << "::handleRequest(Poco::Net::HTTPServerRequest& request, Poco::Net::HTTPServerResponse& response)\n";
262 ostr << "{\n";
263 writeResponse(ostr);
264 writeSession(ostr);
265 if (_page.has("page.precondition"))
266 {
267 ostr << "\tif (!(" << _page.get("page.precondition") << ")) return;\n\n";
268 }
269 writeForm(ostr);
270 ostr << _page.preHandler().str();
271 writeContent(ostr);
272 ostr << "}\n";
273}
274
275
276void CodeWriter::writeFactory(std::ostream& ostr)
277{
278}
279
280
281void CodeWriter::writeManifest(std::ostream& ostr)
282{
283}
284
285
286void CodeWriter::writeSession(std::ostream& ostr)
287{
288}
289
290
291void CodeWriter::writeForm(std::ostream& ostr)
292{
293 if (_page.getBool("page.form", true))
294 {
295 std::string partHandler(_page.get("page.formPartHandler", ""));
296 if (!partHandler.empty())
297 {
298 ostr << "\t" << partHandler << " cpspPartHandler(*this);\n";
299 }
300 ostr << "\tPoco::Net::HTMLForm form(request, request.stream()";
301 if (!partHandler.empty())
302 {
303 ostr << ", cpspPartHandler";
304 }
305 ostr << ");\n";
306 }
307}
308
309
310void CodeWriter::writeResponse(std::ostream& ostr)
311{
312 std::string contentType(_page.get("page.contentType", "text/html"));
313 std::string contentLang(_page.get("page.contentLanguage", ""));
314 std::string cacheControl(_page.get("page.cacheControl", ""));
315 bool buffered(_page.getBool("page.buffered", false));
316 bool chunked(_page.getBool("page.chunked", !buffered));
317 bool compressed(_page.getBool("page.compressed", false));
318 if (buffered) compressed = false;
319 if (compressed) chunked = true;
320
321 if (chunked)
322 {
323 ostr << "\tresponse.setChunkedTransferEncoding(true);\n";
324 }
325
326 ostr << "\tresponse.setContentType(\"" << contentType << "\");\n";
327 if (!contentLang.empty())
328 {
329 ostr << "\tif (request.has(\"Accept-Language\"))\n"
330 << "\t\tresponse.set(\"Content-Language\", \"" << contentLang << "\");\n";
331 }
332 if (compressed)
333 {
334 ostr << "\tbool _compressResponse(request.hasToken(\"Accept-Encoding\", \"gzip\"));\n"
335 << "\tif (_compressResponse) response.set(\"Content-Encoding\", \"gzip\");\n";
336 }
337 if (!cacheControl.empty())
338 {
339 ostr << "\tresponse.set(\"Cache-Control\", \"" << cacheControl << "\");\n";
340 }
341 ostr << "\n";
342}
343
344
345void CodeWriter::writeContent(std::ostream& ostr)
346{
347 bool buffered(_page.getBool("page.buffered", false));
348 bool chunked(_page.getBool("page.chunked", !buffered));
349 bool compressed(_page.getBool("page.compressed", false));
350 int compressionLevel(_page.getInt("page.compressionLevel", 1));
351 if (buffered) compressed = false;
352 if (compressed) chunked = true;
353
354 if (buffered)
355 {
356 ostr << "\tstd::stringstream responseStream;\n";
357 ostr << cleanupHandler(_page.handler().str());
358 if (!chunked)
359 {
360 ostr << "\tresponse.setContentLength(static_cast<int>(responseStream.tellp()));\n";
361 }
362 ostr << "\tPoco::StreamCopier::copyStream(responseStream, response.send());\n";
363 }
364 else if (compressed)
365 {
366 ostr << "\tstd::ostream& _responseStream = response.send();\n"
367 << "\tPoco::DeflatingOutputStream _gzipStream(_responseStream, Poco::DeflatingStreamBuf::STREAM_GZIP, " << compressionLevel << ");\n"
368 << "\tstd::ostream& responseStream = _compressResponse ? _gzipStream : _responseStream;\n";
369 ostr << cleanupHandler(_page.handler().str());
370 ostr << "\tif (_compressResponse) _gzipStream.close();\n";
371 }
372 else
373 {
374 ostr << "\tstd::ostream& responseStream = response.send();\n";
375 ostr << cleanupHandler(_page.handler().str());
376 }
377}
378
379
380std::string CodeWriter::cleanupHandler(std::string handler)
381{
382 static const std::string EMPTY_WRITE("\tresponseStream << \"\";\n");
383 static const std::string NEWLINE_WRITE("\tresponseStream << \"\\n\";\n");
384 static const std::string DOUBLE_NEWLINE_WRITE("\tresponseStream << \"\\n\";\n\tresponseStream << \"\\n\";\n");
385 static const std::string EMPTY;
386
387 // remove empty writes
388 Poco::replaceInPlace(handler, EMPTY_WRITE, EMPTY);
389 // remove consecutive newlines
390 while (handler.find(DOUBLE_NEWLINE_WRITE) != std::string::npos)
391 {
392 Poco::replaceInPlace(handler, DOUBLE_NEWLINE_WRITE, NEWLINE_WRITE);
393 }
394 return handler;
395}
396
397