1//
2// PageCompiler.cpp
3//
4// A compiler that compiler HTML pages containing JSP directives into C++ classes.
5//
6// Copyright (c) 2008, Applied Informatics Software Engineering GmbH.
7// and Contributors.
8//
9// SPDX-License-Identifier: BSL-1.0
10//
11
12
13#include "Poco/Util/Application.h"
14#include "Poco/Util/Option.h"
15#include "Poco/Util/OptionSet.h"
16#include "Poco/Util/HelpFormatter.h"
17#include "Poco/Util/AbstractConfiguration.h"
18#include "Poco/AutoPtr.h"
19#include "Poco/FileStream.h"
20#include "Poco/Path.h"
21#include "Poco/DateTime.h"
22#include "Poco/DateTimeFormatter.h"
23#include "Poco/DateTimeFormat.h"
24#include "Poco/StringTokenizer.h"
25#include "Poco/LineEndingConverter.h"
26#include "Poco/Ascii.h"
27#include "Page.h"
28#include "PageReader.h"
29#include "CodeWriter.h"
30#include "ApacheCodeWriter.h"
31#include "OSPCodeWriter.h"
32#include <sstream>
33#include <iostream>
34#include <memory>
35
36
37using Poco::Util::Application;
38using Poco::Util::Option;
39using Poco::Util::OptionSet;
40using Poco::Util::HelpFormatter;
41using Poco::Util::AbstractConfiguration;
42using Poco::Util::OptionCallback;
43using Poco::AutoPtr;
44using Poco::FileInputStream;
45using Poco::FileOutputStream;
46using Poco::Path;
47using Poco::DateTime;
48using Poco::DateTimeFormatter;
49using Poco::DateTimeFormat;
50using Poco::StringTokenizer;
51using Poco::OutputLineEndingConverter;
52
53
54class CompilerApp: public Application
55{
56public:
57 CompilerApp():
58 _helpRequested(false),
59 _generateOSPCode(false),
60 _generateApacheCode(false),
61 _emitLineDirectives(true)
62 {
63 }
64
65protected:
66 void initialize(Application& self)
67 {
68 loadConfiguration(); // load default configuration files, if present
69 Application::initialize(self);
70 }
71
72 void defineOptions(OptionSet& options)
73 {
74 Application::defineOptions(options);
75
76 options.addOption(
77 Option("help", "h", "Display help information on command line arguments.")
78 .required(false)
79 .repeatable(false)
80 .callback(OptionCallback<CompilerApp>(this, &CompilerApp::handleHelp)));
81
82 options.addOption(
83 Option("define", "D",
84 "Define a configuration property. A configuration property "
85 "defined with this option can be referenced in the input "
86 "page file, using the following syntax: ${<name>}.")
87 .required(false)
88 .repeatable(true)
89 .argument("<name>=<value>")
90 .callback(OptionCallback<CompilerApp>(this, &CompilerApp::handleDefine)));
91
92 options.addOption(
93 Option("config-file", "f", "Load configuration data from the given file.")
94 .required(false)
95 .repeatable(true)
96 .argument("<file>")
97 .callback(OptionCallback<CompilerApp>(this, &CompilerApp::handleConfig)));
98
99 options.addOption(
100 Option("output-dir", "o", "Write output files to directory <dir>.")
101 .required(false)
102 .repeatable(false)
103 .argument("<dir>")
104 .callback(OptionCallback<CompilerApp>(this, &CompilerApp::handleOutputDir)));
105
106 options.addOption(
107 Option("header-output-dir", "H", "Write header file to directory <dir>.")
108 .required(false)
109 .repeatable(false)
110 .argument("<dir>")
111 .callback(OptionCallback<CompilerApp>(this, &CompilerApp::handleHeaderOutputDir)));
112
113 options.addOption(
114 Option("header-prefix", "P", "Prepend the given <prefix> to the header file name in the generated #include directive.")
115 .required(false)
116 .repeatable(false)
117 .argument("<prefix>")
118 .callback(OptionCallback<CompilerApp>(this, &CompilerApp::handleHeaderPrefix)));
119
120 options.addOption(
121 Option("base-file-name", "b", "Use <name> instead of the class name for the output file name.")
122 .required(false)
123 .repeatable(false)
124 .argument("<name>")
125 .callback(OptionCallback<CompilerApp>(this, &CompilerApp::handleBase)));
126
127 options.addOption(
128 Option("osp", "O", "Add factory class definition and implementation for use with the Open Service Platform.")
129 .required(false)
130 .repeatable(false)
131 .callback(OptionCallback<CompilerApp>(this, &CompilerApp::handleOSP)));
132
133 options.addOption(
134 Option("apache", "A", "Add factory class definition and implementation, and shared library manifest for use with ApacheConnector.")
135 .required(false)
136 .repeatable(false)
137 .callback(OptionCallback<CompilerApp>(this, &CompilerApp::handleApache)));
138
139 options.addOption(
140 Option("noline", "N", "Do not include #line directives in generated code.")
141 .required(false)
142 .repeatable(false)
143 .callback(OptionCallback<CompilerApp>(this, &CompilerApp::handleNoLine)));
144 }
145
146 void handleHelp(const std::string& /*name*/, const std::string& /*value*/)
147 {
148 _helpRequested = true;
149 stopOptionsProcessing();
150 }
151
152 void handleDefine(const std::string& /*name*/, const std::string& value)
153 {
154 defineProperty(value);
155 }
156
157 void handleConfig(const std::string& /*name*/, const std::string& value)
158 {
159 loadConfiguration(value);
160 }
161
162 void handleOutputDir(const std::string& /*name*/, const std::string& value)
163 {
164 _outputDir = value;
165 }
166
167 void handleHeaderOutputDir(const std::string& /*name*/, const std::string& value)
168 {
169 _headerOutputDir = value;
170 }
171
172 void handleHeaderPrefix(const std::string& /*name*/, const std::string& value)
173 {
174 _headerPrefix = value;
175 if (!_headerPrefix.empty() && _headerPrefix[_headerPrefix.size() - 1] != '/')
176 _headerPrefix += '/';
177 }
178
179 void handleBase(const std::string& /*name*/, const std::string& value)
180 {
181 _base = value;
182 }
183
184 void handleOSP(const std::string& /*name*/, const std::string& /*value*/)
185 {
186 _generateOSPCode = true;
187 }
188
189 void handleApache(const std::string& /*name*/, const std::string& /*value*/)
190 {
191 _generateApacheCode = true;
192 }
193
194 void handleNoLine(const std::string& /*name*/, const std::string& /*value*/)
195 {
196 _emitLineDirectives = false;
197 }
198
199 void displayHelp()
200 {
201 HelpFormatter helpFormatter(options());
202 helpFormatter.setCommand(commandName());
203 helpFormatter.setUsage("[<option> ...] <file> ...");
204 helpFormatter.setHeader(
205 "\n"
206 "The POCO C++ Server Page Compiler.\n"
207 "Copyright (c) 2008-2018 by Applied Informatics Software Engineering GmbH.\n"
208 "All rights reserved.\n\n"
209 "This program compiles web pages containing embedded C++ code "
210 "into a C++ class that can be used with the HTTP server "
211 "from the POCO Net library. \n\n"
212 "The following command line options are supported:"
213 );
214 helpFormatter.setFooter(
215 "For more information, please see the POCO C++ Libraries "
216 "documentation at <http://pocoproject.org/docs/>."
217 );
218 helpFormatter.setIndent(8);
219 helpFormatter.format(std::cout);
220 }
221
222 void defineProperty(const std::string& def)
223 {
224 std::string name;
225 std::string value;
226 std::string::size_type pos = def.find('=');
227 if (pos != std::string::npos)
228 {
229 name.assign(def, 0, pos);
230 value.assign(def, pos + 1, def.length() - pos);
231 }
232 else name = def;
233 config().setString(name, value);
234 }
235
236 int main(const std::vector<std::string>& args)
237 {
238 if (_helpRequested || args.empty())
239 {
240 displayHelp();
241 return Application::EXIT_OK;
242 }
243
244 for (std::vector<std::string>::const_iterator it = args.begin(); it != args.end(); ++it)
245 {
246 compile(*it);
247 }
248
249 return Application::EXIT_OK;
250 }
251
252 void parse(const std::string& path, Page& page, std::string& clazz)
253 {
254 FileInputStream srcStream(path);
255 PageReader pageReader(page, path);
256 pageReader.emitLineDirectives(_emitLineDirectives);
257 pageReader.parse(srcStream);
258
259 Path p(path);
260
261 if (page.has("page.class"))
262 {
263 clazz = page.get("page.class");
264 }
265 else
266 {
267 clazz = p.getBaseName() + "Handler";
268 clazz[0] = static_cast<char>(Poco::Ascii::toUpper(clazz[0]));
269 }
270 }
271
272 void write(const std::string& path, const Page& page, const std::string& clazz)
273 {
274 Path p(path);
275 config().setString("inputFileName", p.getFileName());
276 config().setString("inputFilePath", p.toString());
277
278 DateTime now;
279 config().setString("dateTime", DateTimeFormatter::format(now, DateTimeFormat::SORTABLE_FORMAT));
280
281 if (page.has("page.class"))
282 {
283 p.setBaseName(clazz);
284 }
285
286 std::unique_ptr<CodeWriter> pCodeWriter(createCodeWriter(page, clazz));
287
288 if (!_outputDir.empty())
289 {
290 p = Path(_outputDir, p.getBaseName());
291 }
292
293 if (!_base.empty())
294 {
295 p.setBaseName(_base);
296 }
297
298 p.setExtension("cpp");
299 std::string implPath = p.toString();
300 std::string implFileName = p.getFileName();
301
302 if (!_headerOutputDir.empty())
303 {
304 p = Path(_headerOutputDir, p.getBaseName());
305 }
306 p.setExtension("h");
307 std::string headerPath = p.toString();
308 std::string headerFileName = p.getFileName();
309
310 config().setString("outputFileName", implFileName);
311 config().setString("outputFilePath", implPath);
312 FileOutputStream implStream(implPath);
313 OutputLineEndingConverter implLEC(implStream);
314 writeFileHeader(implLEC);
315 pCodeWriter->writeImpl(implLEC, _headerPrefix + headerFileName);
316
317 config().setString("outputFileName", headerFileName);
318 config().setString("outputFilePath", headerPath);
319 FileOutputStream headerStream(headerPath);
320 OutputLineEndingConverter headerLEC(headerStream);
321 writeFileHeader(headerLEC);
322 pCodeWriter->writeHeader(headerLEC, headerFileName);
323 }
324
325 void compile(const std::string& path)
326 {
327 Page page;
328 std::string clazz;
329 parse(path, page, clazz);
330 write(path, page, clazz);
331
332 FileInputStream srcStream(path);
333 PageReader pageReader(page, path);
334 pageReader.emitLineDirectives(_emitLineDirectives);
335 pageReader.parse(srcStream);
336
337 }
338
339 void writeFileHeader(std::ostream& ostr)
340 {
341 std::string fileHeader = config().getString("PageCompiler.fileHeader", "");
342 if (!fileHeader.empty())
343 {
344 ostr << fileHeader << std::endl;
345 ostr << "\n\n";
346 }
347 }
348
349 CodeWriter* createCodeWriter(const Page& page, const std::string& clazz)
350 {
351 if (_generateOSPCode)
352 return new OSPCodeWriter(page, clazz);
353 else if (_generateApacheCode)
354 return new ApacheCodeWriter(page, clazz);
355 else
356 return new CodeWriter(page, clazz);
357 }
358
359private:
360 bool _helpRequested;
361 bool _generateOSPCode;
362 bool _generateApacheCode;
363 bool _emitLineDirectives;
364 std::string _outputDir;
365 std::string _headerOutputDir;
366 std::string _headerPrefix;
367 std::string _base;
368};
369
370
371POCO_APP_MAIN(CompilerApp)
372