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 | |
18 | using Poco::Path; |
19 | using Poco::StringTokenizer; |
20 | |
21 | |
22 | CodeWriter::(const Page& page, const std::string& clazz): |
23 | _page(page), |
24 | _class(clazz) |
25 | { |
26 | } |
27 | |
28 | |
29 | CodeWriter::~CodeWriter() |
30 | { |
31 | } |
32 | |
33 | |
34 | void CodeWriter::(std::ostream& ostr, const std::string& ) |
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 | |
54 | void CodeWriter::writeImpl(std::ostream& ostr, const std::string& ) |
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 | |
91 | void 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 | |
105 | void 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 | |
121 | void CodeWriter::beginGuard(std::ostream& ostr, const std::string& ) |
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 | |
134 | void CodeWriter::endGuard(std::ostream& ostr, const std::string& ) |
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 | |
145 | void 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 | |
171 | void 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 | |
181 | void 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 | |
190 | void CodeWriter::(std::ostream& ostr) |
191 | { |
192 | ostr << "#include \"Poco/Net/HTTPRequestHandler.h\"\n" ; |
193 | } |
194 | |
195 | |
196 | void 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 | |
206 | void 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 | |
224 | void CodeWriter::writeFactoryClass(std::ostream& ostr) |
225 | { |
226 | } |
227 | |
228 | |
229 | void 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 | |
237 | void 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 | |
259 | void 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 | |
276 | void CodeWriter::writeFactory(std::ostream& ostr) |
277 | { |
278 | } |
279 | |
280 | |
281 | void CodeWriter::writeManifest(std::ostream& ostr) |
282 | { |
283 | } |
284 | |
285 | |
286 | void CodeWriter::writeSession(std::ostream& ostr) |
287 | { |
288 | } |
289 | |
290 | |
291 | void 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 | |
310 | void 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 | |
345 | void 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 | |
380 | std::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 | |