From fdc2e6c0eb2a375f74c8969275d81b500fcc0190 Mon Sep 17 00:00:00 2001 From: Guenter Obiltschnig Date: Wed, 16 Jan 2008 18:19:08 +0000 Subject: [PATCH] added PageCompiler --- PageCompiler/Makefile | 17 + PageCompiler/PageCompiler_vs71.sln | 21 + PageCompiler/PageCompiler_vs71.vcproj | 280 +++++++++++ PageCompiler/PageCompiler_vs80.sln | 20 + PageCompiler/PageCompiler_vs80.vcproj | 229 +++++++++ PageCompiler/cpspc.properties | 1 + PageCompiler/doc/PageCompilerUserGuide.page | 270 ++++++++++ PageCompiler/src/Page.cpp | 357 ++++++++++++++ PageCompiler/src/Page.h | 123 +++++ PageCompiler/src/PageCompiler.cpp | 515 ++++++++++++++++++++ PageCompiler/test/header.inc | 3 + PageCompiler/test/test.cpsp | 27 + 12 files changed, 1863 insertions(+) create mode 100644 PageCompiler/Makefile create mode 100644 PageCompiler/PageCompiler_vs71.sln create mode 100644 PageCompiler/PageCompiler_vs71.vcproj create mode 100644 PageCompiler/PageCompiler_vs80.sln create mode 100644 PageCompiler/PageCompiler_vs80.vcproj create mode 100644 PageCompiler/cpspc.properties create mode 100644 PageCompiler/doc/PageCompilerUserGuide.page create mode 100644 PageCompiler/src/Page.cpp create mode 100644 PageCompiler/src/Page.h create mode 100644 PageCompiler/src/PageCompiler.cpp create mode 100644 PageCompiler/test/header.inc create mode 100644 PageCompiler/test/test.cpsp diff --git a/PageCompiler/Makefile b/PageCompiler/Makefile new file mode 100644 index 000000000..f5c4eb46b --- /dev/null +++ b/PageCompiler/Makefile @@ -0,0 +1,17 @@ +# +# Makefile +# +# $Id: //poco/Main/PageCompiler/Makefile#1 $ +# +# Makefile for Poco C++ Server Page Compiler +# + +include $(POCO_BASE)/build/rules/global + +objects = Page PageCompiler + +target = cpspc +target_version = 1 +target_libs = PocoNet PocoUtil PocoXML PocoFoundation + +include $(POCO_BASE)/build/rules/exec diff --git a/PageCompiler/PageCompiler_vs71.sln b/PageCompiler/PageCompiler_vs71.sln new file mode 100644 index 000000000..b42d3a98f --- /dev/null +++ b/PageCompiler/PageCompiler_vs71.sln @@ -0,0 +1,21 @@ +Microsoft Visual Studio Solution File, Format Version 8.00 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PageCompiler", "PageCompiler_vs71.vcproj", "{73408DCF-2E65-47AF-BF38-7466DAB8D5CB}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfiguration) = preSolution + debug_shared = debug_shared + release_shared = release_shared + EndGlobalSection + GlobalSection(ProjectConfiguration) = postSolution + {73408DCF-2E65-47AF-BF38-7466DAB8D5CB}.debug_shared.ActiveCfg = debug_shared|Win32 + {73408DCF-2E65-47AF-BF38-7466DAB8D5CB}.debug_shared.Build.0 = debug_shared|Win32 + {73408DCF-2E65-47AF-BF38-7466DAB8D5CB}.release_shared.ActiveCfg = release_shared|Win32 + {73408DCF-2E65-47AF-BF38-7466DAB8D5CB}.release_shared.Build.0 = release_shared|Win32 + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + EndGlobalSection + GlobalSection(ExtensibilityAddIns) = postSolution + EndGlobalSection +EndGlobal diff --git a/PageCompiler/PageCompiler_vs71.vcproj b/PageCompiler/PageCompiler_vs71.vcproj new file mode 100644 index 000000000..1001b71c5 --- /dev/null +++ b/PageCompiler/PageCompiler_vs71.vcproj @@ -0,0 +1,280 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/PageCompiler/PageCompiler_vs80.sln b/PageCompiler/PageCompiler_vs80.sln new file mode 100644 index 000000000..a01dd4e33 --- /dev/null +++ b/PageCompiler/PageCompiler_vs80.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 9.00 +# Visual Studio 2005 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PageCompiler", "PageCompiler_vs80.vcproj", "{E12E5C71-79A4-495A-848F-F1710111E610}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + debug_shared|Win32 = debug_shared|Win32 + release_shared|Win32 = release_shared|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {E12E5C71-79A4-495A-848F-F1710111E610}.debug_shared|Win32.ActiveCfg = debug_shared|Win32 + {E12E5C71-79A4-495A-848F-F1710111E610}.debug_shared|Win32.Build.0 = debug_shared|Win32 + {E12E5C71-79A4-495A-848F-F1710111E610}.release_shared|Win32.ActiveCfg = release_shared|Win32 + {E12E5C71-79A4-495A-848F-F1710111E610}.release_shared|Win32.Build.0 = release_shared|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/PageCompiler/PageCompiler_vs80.vcproj b/PageCompiler/PageCompiler_vs80.vcproj new file mode 100644 index 000000000..3dfec47d4 --- /dev/null +++ b/PageCompiler/PageCompiler_vs80.vcproj @@ -0,0 +1,229 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/PageCompiler/cpspc.properties b/PageCompiler/cpspc.properties new file mode 100644 index 000000000..a5743b083 --- /dev/null +++ b/PageCompiler/cpspc.properties @@ -0,0 +1 @@ +PageCompiler.fileHeader = //\n// ${outputFileName}\n//\n// This file has been generated from ${inputFileName} on ${dateTime}.\n// diff --git a/PageCompiler/doc/PageCompilerUserGuide.page b/PageCompiler/doc/PageCompilerUserGuide.page new file mode 100644 index 000000000..1403b06ed --- /dev/null +++ b/PageCompiler/doc/PageCompilerUserGuide.page @@ -0,0 +1,270 @@ +POCO C++ Server Page Compiler User Guide +PageCompiler + +!!!Introduction +PageCompiler is a command line tool that translates HTML files (and other kinds of files), into +C++ code, more precisely, subclasses of Poco::Net::HTTPRequestHandler. +The source files can contain special directives that allow it to include C++ code into +the source file. The syntax of this directives is based on the syntax used for +Java Server Pages (JSP) and PHP. + +The following introductory sample shows the code for a simple page that displays the +current date and time. + + <%@ page class="TimeHandler" %> + <%! + #include "Poco/DateTime.h" + #include "Poco/DateTimeFormatter.h" + #include "Poco/DateTimeFormat.h" + + + using Poco::DateTime; + using Poco::DateTimeFormatter; + using Poco::DateTimeFormat; + %> + + <% + DateTime now; + std::string dt(DateTimeFormatter::format(now, DateTimeFormat::SORTABLE_FORMAT)); + %> + + + Time Sample + + +

Time Sample

+

<%= dt %>

+ + +---- + +Sending the above code to the page compiler will generate two files, a header file +(<[TimeHandler.h]>) and an implementation file (<[TimeHandler.cpp]>). +The files define a subclass of Poco::Net::HTTPRequestHandler named <[TimeHandler]>. +The generated <[handleRequest]> member function contains code to send the HTML +code contained in the source file to the client, as well as the C++ code fragments found +in between the Scriptlet tags. + + +!!!C++ Server Page Syntax + +The following special tags are supported in a C++ server page (CPSP) file. + +!!Hidden Comment + +A hidden comment documents the CPSP file, but is not sent to the client. + + <%-- --%> +---- + +!!Implementation Declaration + +An implementation declaration is copied to the implementation file immediately after +the block containing the standard <[#include]> directives. +It is used to include additional header files and <[using]> declarations, +as well as to define classes needed later on. + + <%! + + ... + %> +---- + +!!Header Declaration + +A header declaration is copied to the header file immediately after the +block containing the standard <[#include]> directives. +It is usually used to include the header file containing the definition +of the base class for the request handler, if a custom base class +is required. + + <%!! + + ... + %> +---- + +!!Expression + +The result of any valid C++ expression can be directly inserted into the page, +provided the result can be written to an output stream. Note that the expression +must not end with a semicolon. + + <%= %> +---- + +!!Scriptlet + +Arbitrary C++ code fragments can be included using the Scriptlet directive. + + <% + + ... + %> +---- + +!!Include Directive + +Another CPSP file can be included into the current file using the Include +Directive. + + <%@ include page="" %> +---- + +If the given path is relative, it is considered relative to the path of +the currently processed CPSP file. + +!!Page Directive + +The Page Directive allows the definition of attributes that control +various aspects of C++ code generation. + + <%@ page ="" ... %> +---- + +The following page attributes are supported: + +!class + +Specifies the name of the generated class. +Defaults to the base name of the source file with the word "Handler" appended. + +!namespace + +If specified, sets the namespace where the generated classes will be in. +No namespace will be used if omitted. + +!baseClass + +Specifies the name of the class used as the base class for the generated +request handler class. +Defaults to Poco::Net::HTTPRequestHandler. Do not forget to add a Header Declaration +containing an <[#include]> directive for the header file containing the definition +of that class, otherwise the generated code won't compile. + +!ctorArg + +Allows to specify the type of a single argument being passed to the constructor +of the generated request handler class. Can only be used together with <[baseClass]>. +The argument is passed on to the constructor of the base class, therefore, one of the +constructors of the base class must also accept a single argument of the specified type. + +!export + +Allows to specify a DLL import/export directive that is being added to the request +handler class definition. Useful for exporting a request handler class from a +Windows DLL. + +!form + +Enable or disable automatic form handling. If enabled, which is the default, +a Poco::Net::HTMLForm object is automatically created in the request handler +and accessible through a variable named <[form]>. +Set the value to <[false]> to disable form handling. + +!formPartHandler + +Allows you to pass a Poco::Net::PartHandler object to the form object for +processing file uploads. A subclass of Poco::Net::PartHandler must be +defined (using an Implementation Declaration), and the constructor of the part +handler must take a (const) reference to the request handler instance as argument. + +!contentType + +Allows you to specify the MIME content type for the page. Defaults to text/html. + +!chunked + +Allows you to specify whether the response is sent using chunked transfer encoding. +Defaults to <[true]>. +Set the value to <[false]> to disable chunked transfer encoding. + +!session (OSP only) + +For use with the POCO Open Service Platform only. + +Specifies the identifier of the session obtained from the OSP Web Session Manager. +If specified, a Poco::OSP::Web::WebSession object will be available in the +request handler through a variable named <[session]>. The variable is of type +Poco::OSP::Web::WebSession::Ptr. + +!sessionTimeout (OSP only) + +For use with the POCO Open Service Platform only. + +Specifies the session timeout in minutes. + + +!!Implicit Objects + +The following objects are available in the handler code. + +!request + +The HTTP request object - an instance of Poco::Net::HTTPServerRequest. + +!response + +The HTTP response object - an instance of Poco::Net::HTTPServerRequest. + +!form + +An instance of Poco::Net::HTMLForm for processing form arguments. +Only available if form processing has not been disabled by +setting the <[form]> page attribute to <[false]>. + +!session (OSP only) + +An instance of Poco::OSP::Web::WebSession::Ptr for accessing the +Poco::OSP::Web::WebSession object for the current session. +Only available with the POCO Open Service Platform, and if the +<[session]> page attribute has been specified. + + +!!!Invoking the Page Compiler + +The Page Compiler is invoked from the command line. The file names of the +CPSP files to be compiled are specified as arguments. + +A number of options control the code generation. Options are specified +using the usual command-line option syntax for the current operating +system (e.g., <[/help]> on Windows, <[--help]> or <[-h]> on Unix). + + * help (h): display usage information + * define (D): define a configuration property + * config-file (f): load configuration properties from a file + * osp (O): add factory class definition/implementation for use with OSP + * apache (A): add factory class definition/implementation and shared library manifest for use with ApacheConnector + +!!Configuration Properties + +The Page Compiler supports one configuration property, named +<[PageCompiler.fileHeader]>, to optionally specify a header that is +included in every generated file. + +The file header can contain references to other configuration properties, +using the usual property syntax: <[${property}]>. + +For example, invoking the Page Compiler with the following configuration +file: + + PageCompiler.fileHeader = //\n// ${outputFileName}\n//\n +---- + +places the following header at the beginning of each generated file +(<[]> is replaced with the actual name of the file): + + // + // + // +---- + +The following pre-defined properties can be used in the file header: + + * <[${inputFileName}]>: the name of the input file (with directories removed) + * <[${inputFilePath}]>: the complete path of the input file + * <[${dateTime}]>: the current date and time (YYYY-MM-DD HH:MM:SS) + * <[${outputFileName}]>: the name of the current output file (header or implementation file), with + directories removed + * <[${outputFilePath}]>: the complete path of the current output file + diff --git a/PageCompiler/src/Page.cpp b/PageCompiler/src/Page.cpp new file mode 100644 index 000000000..0d5df6e40 --- /dev/null +++ b/PageCompiler/src/Page.cpp @@ -0,0 +1,357 @@ +// +// Page.cpp +// +// $Id: //poco/Main/PageCompiler/src/Page.cpp#1 $ +// +// Copyright (c) 2007, Applied Informatics Software Engineering GmbH. +// and Contributors. +// +// Permission is hereby granted, free of charge, to any person or organization +// obtaining a copy of the software and accompanying documentation covered by +// this license (the "Software") to use, reproduce, display, distribute, +// execute, and transmit the Software, and to prepare derivative works of the +// Software, and to permit third-parties to whom the Software is furnished to +// do so, all subject to the following: +// +// The copyright notices in the Software and this entire statement, including +// the above license grant, this restriction and the following disclaimer, +// must be included in all copies of the Software, in whole or in part, and +// all derivative works of the Software, unless such copies or derivative +// works are solely in the form of machine-executable object code generated by +// a source language processor. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +// SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// + + +#include "Page.h" +#include "Poco/FileStream.h" +#include "Poco/CountingStream.h" +#include "Poco/Path.h" +#include "Poco/Exception.h" +#include +#include + + +const std::string Page::MARKUP_BEGIN("\tostr << \""); +const std::string Page::MARKUP_END("\";\n"); +const std::string Page::EXPR_BEGIN("\tostr << "); +const std::string Page::EXPR_END(";\n"); + + +Page::Page(const std::string& basePath) +{ + _headerDecls.reserve(4096); + _implDecls.reserve(8192); + _paths.push_back(basePath); +} + + +Page::~Page() +{ +} + + +void Page::addHeaderDecls(const std::string& decls) +{ + _headerDecls.append("\n"); + _headerDecls.append(decls); +} + + +void Page::addImplDecls(const std::string& decls) +{ + _implDecls.append("\n"); + _implDecls.append(decls); +} + + +void Page::addAttrs(const std::string& attrs) +{ + _attrs.append(attrs); +} + + +void Page::parse(std::istream& pageStream) +{ + std::ostringstream handlerStream; + parse(pageStream, handlerStream); + _handler = handlerStream.str(); +} + + +void Page::parse(std::istream& pageStream, std::ostream& handlerStream) +{ + ParsingState state = STATE_MARKUP; + + _attrs.clear(); + handlerStream << MARKUP_BEGIN; + + Poco::CountingInputStream countingPageStream(pageStream); + std::string token; + nextToken(pageStream, token); + while (!token.empty()) + { + if (token == "<%") + { + if (state == STATE_MARKUP) + { + handlerStream << MARKUP_END; + state = STATE_BLOCK; + } + else handlerStream << token; + } + else if (token == "<%!") + { + if (state == STATE_MARKUP) + { + handlerStream << MARKUP_END; + state = STATE_IMPLDECL; + } + else handlerStream << token; + } + else if (token == "<%!!") + { + if (state == STATE_MARKUP) + { + handlerStream << MARKUP_END; + state = STATE_HDRDECL; + } + else handlerStream << token; + } + else if (token == "<%--") + { + if (state == STATE_MARKUP) + { + handlerStream << MARKUP_END; + state = STATE_COMMENT; + } + else handlerStream << token; + } + else if (token == "<%@") + { + if (state == STATE_MARKUP) + { + handlerStream << MARKUP_END; + state = STATE_ATTR; + } + else handlerStream << token; + } + else if (token == "<%=") + { + if (state == STATE_MARKUP) + { + handlerStream << MARKUP_END; + handlerStream << EXPR_BEGIN; + state = STATE_EXPR; + } + else handlerStream << token; + } + else if (token == "%>") + { + if (state == STATE_EXPR) + { + handlerStream << EXPR_END; + handlerStream << MARKUP_BEGIN; + state = STATE_MARKUP; + } + else if (state == STATE_ATTR) + { + parseAttributes(handlerStream); + _attrs.clear(); + handlerStream << MARKUP_BEGIN; + state = STATE_MARKUP; + } + else if (state != STATE_MARKUP) + { + handlerStream << MARKUP_BEGIN; + state = STATE_MARKUP; + } + else handlerStream << token; + } + else + { + switch (state) + { + case STATE_MARKUP: + if (token == "\n") + { + handlerStream << "\\n"; + handlerStream << MARKUP_END; + handlerStream << MARKUP_BEGIN; + } + else if (token == "\t") + { + handlerStream << "\\t"; + } + else if (token == "\"") + { + handlerStream << "\\\""; + } + else if (token != "\r") + { + handlerStream << token; + } + break; + case STATE_IMPLDECL: + _implDecls += token; + break; + case STATE_HDRDECL: + _headerDecls += token; + break; + case STATE_BLOCK: + handlerStream << token; + break; + case STATE_EXPR: + handlerStream << token; + break; + case STATE_COMMENT: + break; + case STATE_ATTR: + _attrs += token; + break; + } + } + nextToken(pageStream, token); + } + + if (state == STATE_MARKUP) + { + handlerStream << MARKUP_END; + } + else throw Poco::SyntaxException("unclosed meta or code block", where()); +} + + +void Page::parseAttributes(std::ostream& handlerStream) +{ + static const int eof = std::char_traits::eof(); + + std::string basename; + std::istringstream istr(_attrs); + int ch = istr.get(); + while (ch != eof && std::isspace(ch)) ch = istr.get(); + while (ch != eof && std::isalnum(ch)) { basename += (char) ch; ch = istr.get(); } + while (ch != eof && std::isspace(ch)) ch = istr.get(); + while (ch != eof) + { + std::string name(basename + "."); + std::string value; + while (ch != eof && std::isalnum(ch)) { name += (char) ch; ch = istr.get(); } + while (ch != eof && std::isspace(ch)) ch = istr.get(); + if (ch != '=') throw Poco::SyntaxException("bad attribute syntax: '=' expected", where()); + ch = istr.get(); + while (ch != eof && std::isspace(ch)) ch = istr.get(); + if (ch != '"') throw Poco::SyntaxException("bad attribute syntax: '\"' expected", where()); + ch = istr.get(); + while (ch != eof && ch != '"') { value += (char) ch; ch = istr.get(); } + if (ch != '"') throw Poco::SyntaxException("bad attribute syntax: '\"' expected", where()); + ch = istr.get(); + handleAttribute(name, value, handlerStream); + while (ch != eof && std::isspace(ch)) ch = istr.get(); + } +} + + +void Page::nextToken(std::istream& istr, std::string& token) +{ + token.clear(); + int ch = istr.get(); + if (ch != -1) + { + if (ch == '<' && istr.peek() == '%') + { + token += "<%"; + ch = istr.get(); + ch = istr.peek(); + switch (ch) + { + case '!': + ch = istr.get(); + token += (char) ch; + if (istr.peek() == '!') + { + ch = istr.get(); + token += (char) ch; + } + break; + case '=': + ch = istr.get(); + token += (char) ch; + break; + case '@': + ch = istr.get(); + token += (char) ch; + break; + case '-': + ch = istr.get(); + token += (char) ch; + if (istr.peek() == '-') + { + ch = istr.get(); + token += (char) ch; + } + break; + } + } + else if (ch == '%' && istr.peek() == '>') + { + token += "%>"; + ch = istr.get(); + } + else token += (char) ch; + } +} + + +void Page::handleAttribute(const std::string& name, const std::string& value, std::ostream& handlerStream) +{ + if (name == "include.page") + { + include(value, handlerStream); + } + else + { + set(name, value); + } +} + + +void Page::include(const std::string& path, std::ostream& handlerStream) +{ + Poco::Path currentPath(_paths.back()); + Poco::Path includePath(path); + currentPath.resolve(includePath); + handlerStream << "\t// begin include " << currentPath.toString() << "\n"; + _paths.push_back(currentPath.toString()); + if (_paths.size() > 100) throw Poco::ApplicationException("Too many includes", where()); + Poco::FileInputStream includeStream(currentPath.toString()); + parse(includeStream, handlerStream); + _paths.pop_back(); + handlerStream << "\t// end include " << currentPath.toString() << "\n"; +} + + +std::string Page::where() +{ + std::string result("in file '"); + std::vector::reverse_iterator it = _paths.rbegin(); + result += *it; + result += "'"; + ++it; + while (it != _paths.rend()) + { + result += "\n\tincluded from file "; + result += "'"; + result += *it; + result += "'"; + ++it; + } + return result; +} diff --git a/PageCompiler/src/Page.h b/PageCompiler/src/Page.h new file mode 100644 index 000000000..ce6925324 --- /dev/null +++ b/PageCompiler/src/Page.h @@ -0,0 +1,123 @@ +// +// Page.h +// +// $Id: //poco/Main/PageCompiler/src/Page.h#1 $ +// +// Copyright (c) 2007, Applied Informatics Software Engineering GmbH. +// and Contributors. +// +// Permission is hereby granted, free of charge, to any person or organization +// obtaining a copy of the software and accompanying documentation covered by +// this license (the "Software") to use, reproduce, display, distribute, +// execute, and transmit the Software, and to prepare derivative works of the +// Software, and to permit third-parties to whom the Software is furnished to +// do so, all subject to the following: +// +// The copyright notices in the Software and this entire statement, including +// the above license grant, this restriction and the following disclaimer, +// must be included in all copies of the Software, in whole or in part, and +// all derivative works of the Software, unless such copies or derivative +// works are solely in the form of machine-executable object code generated by +// a source language processor. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +// SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// + + +#ifndef Page_INCLUDED +#define Page_INCLUDED + + +#include "Poco/Net/NameValueCollection.h" +#include +#include +#include + + +class Page: public Poco::Net::NameValueCollection + /// This class represents a server page consisting of + /// handler code and declarations, as well as page attributes. +{ +public: + Page(const std::string& basePath); + /// Creates a Page. + + ~Page(); + /// Destroys the Page. + + const std::string& headerDecls() const; + /// Returns the user-specified declarations for the header file. + + const std::string& implDecls() const; + /// Returns the user-specified declarations for the source file. + + const std::string& handler() const; + /// Returns the request handler code. + + void parse(std::istream& istr); + /// Parses a HTML file containing server page directives. + +protected: + enum ParsingState + { + STATE_MARKUP, + STATE_IMPLDECL, + STATE_HDRDECL, + STATE_BLOCK, + STATE_EXPR, + STATE_COMMENT, + STATE_ATTR + }; + + virtual void handleAttribute(const std::string& name, const std::string& value, std::ostream& handlerStream); + void include(const std::string& path, std::ostream& handlerStream); + void parse(std::istream& pageStream, std::ostream& handlerStream); + void addHeaderDecls(const std::string& decls); + void addImplDecls(const std::string& decls); + void addAttrs(const std::string& attrs); + void parseAttributes(std::ostream& handlerStream); + void nextToken(std::istream& istr, std::string& token); + std::string where(); + + static const std::string MARKUP_BEGIN; + static const std::string MARKUP_END; + static const std::string EXPR_BEGIN; + static const std::string EXPR_END; + +private: + std::string _headerDecls; + std::string _implDecls; + std::string _handler; + std::string _attrs; + std::vector _paths; +}; + + +// +// inlines +// +inline const std::string& Page::headerDecls() const +{ + return _headerDecls; +} + + +inline const std::string& Page::implDecls() const +{ + return _implDecls; +} + + +inline const std::string& Page::handler() const +{ + return _handler; +} + + +#endif // Page_INCLUDED diff --git a/PageCompiler/src/PageCompiler.cpp b/PageCompiler/src/PageCompiler.cpp new file mode 100644 index 000000000..05018ade3 --- /dev/null +++ b/PageCompiler/src/PageCompiler.cpp @@ -0,0 +1,515 @@ +// +// PageCompiler.cpp +// +// $Id: //poco/Main/PageCompiler/src/PageCompiler.cpp#1 $ +// +// A compiler that compiler HTML pages containing JSP directives into C++ classes. +// +// Copyright (c) 2007, Applied Informatics Software Engineering GmbH. +// and Contributors. +// +// Permission is hereby granted, free of charge, to any person or organization +// obtaining a copy of the software and accompanying documentation covered by +// this license (the "Software") to use, reproduce, display, distribute, +// execute, and transmit the Software, and to prepare derivative works of the +// Software, and to permit third-parties to whom the Software is furnished to +// do so, all subject to the following: +// +// The copyright notices in the Software and this entire statement, including +// the above license grant, this restriction and the following disclaimer, +// must be included in all copies of the Software, in whole or in part, and +// all derivative works of the Software, unless such copies or derivative +// works are solely in the form of machine-executable object code generated by +// a source language processor. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +// SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// + + +#include "Poco/Util/Application.h" +#include "Poco/Util/Option.h" +#include "Poco/Util/OptionSet.h" +#include "Poco/Util/HelpFormatter.h" +#include "Poco/Util/AbstractConfiguration.h" +#include "Poco/AutoPtr.h" +#include "Poco/FileStream.h" +#include "Poco/Path.h" +#include "Poco/DateTime.h" +#include "Poco/DateTimeFormatter.h" +#include "Poco/DateTimeFormat.h" +#include "Poco/StringTokenizer.h" +#include "Poco/LineEndingConverter.h" +#include "Page.h" +#include +#include +#include + + +using Poco::Util::Application; +using Poco::Util::Option; +using Poco::Util::OptionSet; +using Poco::Util::HelpFormatter; +using Poco::Util::AbstractConfiguration; +using Poco::Util::OptionCallback; +using Poco::AutoPtr; +using Poco::FileInputStream; +using Poco::FileOutputStream; +using Poco::Path; +using Poco::DateTime; +using Poco::DateTimeFormatter; +using Poco::DateTimeFormat; +using Poco::StringTokenizer; +using Poco::OutputLineEndingConverter; + + +class CompilerApp: public Application +{ +public: + CompilerApp(): + _helpRequested(false), + _generateOSPCode(false), + _generateApacheCode(false) + { + } + +protected: + void initialize(Application& self) + { + loadConfiguration(); // load default configuration files, if present + Application::initialize(self); + } + + void defineOptions(OptionSet& options) + { + Application::defineOptions(options); + + options.addOption( + Option("help", "h", "display help information on command line arguments") + .required(false) + .repeatable(false) + .callback(OptionCallback(this, &CompilerApp::handleHelp))); + + options.addOption( + Option("define", "D", "define a property") + .required(false) + .repeatable(true) + .argument("name=value") + .callback(OptionCallback(this, &CompilerApp::handleDefine))); + + options.addOption( + Option("config-file", "f", "load configuration data from a file") + .required(false) + .repeatable(true) + .argument("file") + .callback(OptionCallback(this, &CompilerApp::handleConfig))); + + options.addOption( + Option("osp", "O", "add factory class definition/implementation for use with OSP") + .required(false) + .repeatable(false) + .callback(OptionCallback(this, &CompilerApp::handleOSP))); + + options.addOption( + Option("apache", "A", "add factory class definition/implementation and shared library manifest for use with ApacheConnector") + .required(false) + .repeatable(false) + .callback(OptionCallback(this, &CompilerApp::handleApache))); + } + + void handleHelp(const std::string& name, const std::string& value) + { + _helpRequested = true; + displayHelp(); + stopOptionsProcessing(); + } + + void handleDefine(const std::string& name, const std::string& value) + { + defineProperty(value); + } + + void handleConfig(const std::string& name, const std::string& value) + { + loadConfiguration(value); + } + + void handleOSP(const std::string& name, const std::string& value) + { + _generateOSPCode = true; + } + + void handleApache(const std::string& name, const std::string& value) + { + _generateApacheCode = true; + } + + void displayHelp() + { + HelpFormatter helpFormatter(options()); + helpFormatter.setCommand(commandName()); + helpFormatter.setUsage("OPTIONS PAGES"); + helpFormatter.setHeader("The POCO C++ Server Page Compiler."); + helpFormatter.format(std::cout); + } + + void defineProperty(const std::string& def) + { + std::string name; + std::string value; + std::string::size_type pos = def.find('='); + if (pos != std::string::npos) + { + name.assign(def, 0, pos); + value.assign(def, pos + 1, def.length() - pos); + } + else name = def; + config().setString(name, value); + } + + int main(const std::vector& args) + { + if (_helpRequested) + return Application::EXIT_OK; + + for (std::vector::const_iterator it = args.begin(); it != args.end(); ++it) + { + compile(*it); + } + + return Application::EXIT_OK; + } + + void compile(const std::string& path) + { + Page page(path); + + FileInputStream srcStream(path); + page.parse(srcStream); + + Path p(path); + config().setString("inputFileName", p.getFileName()); + config().setString("inputFilePath", p.toString()); + + DateTime now; + config().setString("dateTime", DateTimeFormatter::format(now, DateTimeFormat::SORTABLE_FORMAT)); + + std::string clazz; + if (page.has("page.class")) + { + clazz = page.get("page.class"); + p.setBaseName(clazz); + } + else + { + clazz = p.getBaseName() + "Handler"; + clazz[0] = std::toupper(clazz[0]); + } + + p.setExtension("h"); + std::string headerPath = p.toString(); + std::string headerFileName = p.getFileName(); + config().setString("outputFileName", p.getFileName()); + config().setString("outputFilePath", headerPath); + FileOutputStream headerStream(headerPath); + OutputLineEndingConverter headerLEC(headerStream); + writeHeader(headerLEC, page, clazz, p.getFileName()); + + p.setExtension("cpp"); + std::string implPath = p.toString(); + config().setString("outputFileName", p.getFileName()); + config().setString("outputFilePath", implPath); + FileOutputStream implStream(implPath); + OutputLineEndingConverter implLEC(implStream); + writeImpl(implLEC, page, clazz, headerFileName); + } + + void writeHeader(std::ostream& ostr, Page& page, const std::string& clazz, const std::string& headerFileName) + { + std::string fileHeader = config().getString("PageCompiler.fileHeader", ""); + if (!fileHeader.empty()) + { + ostr << fileHeader << std::endl; + ostr << "\n\n"; + } + Path p(headerFileName); + std::string guard(p.getBaseName()); + guard += "_INCLUDED"; + ostr << "#ifndef " << guard << "\n"; + ostr << "#define " << guard << "\n"; + ostr << "\n\n"; + ostr << "#include \"Poco/Net/HTTPRequestHandler.h\"\n"; + + if (_generateOSPCode) + { + ostr << "#include \"Poco/OSP/Web/WebRequestHandlerFactory.h\"\n"; + ostr << "#include \"Poco/OSP/BundleContext.h\"\n"; + } + else if (_generateApacheCode) + { + ostr << "#include \"Poco/Net/HTTPRequestHandlerFactory.h\"\n"; + } + + ostr << "\n\n"; + + const std::string& decls = page.headerDecls(); + if (!decls.empty()) + { + ostr << decls; + ostr << "\n\n"; + } + + beginNamespace(ostr, page); + + std::string base(page.get("page.baseClass", "Poco::Net::HTTPRequestHandler")); + std::string ctorArg; + if (_generateOSPCode) + ctorArg = "Poco::OSP::BundleContext::Ptr"; + else + ctorArg = page.get("page.ctorArg", ""); + + std::string exprt(page.get("page.export", "")); + if (!exprt.empty()) exprt += ' '; + + ostr << "class " << exprt << clazz << ": public " << base << "\n"; + ostr << "{\n"; + ostr << "public:\n"; + if (!ctorArg.empty()) + { + ostr << "\t" << clazz << "(" << ctorArg << ");\n"; + ostr << "\n"; + } + ostr << "\tvoid handleRequest(Poco::Net::HTTPServerRequest& request, Poco::Net::HTTPServerResponse& response);\n"; + + if (_generateOSPCode) + writeOSPHeader(ostr); + + ostr << "};\n"; + + if (_generateOSPCode) + { + ostr << "\n\n"; + writeFactoryHeader(ostr, page, clazz, "Poco::OSP::Web::WebRequestHandlerFactory"); + } + else if (_generateApacheCode) + { + ostr << "\n\n"; + writeFactoryHeader(ostr, page, clazz, "Poco::Net::HTTPRequestHandlerFactory"); + } + + endNamespace(ostr, page); + + ostr << "\n\n"; + ostr << "#endif // " << guard << "\n"; + } + + void writeImpl(std::ostream& ostr, Page& page, const std::string& clazz, const std::string& headerFileName) + { + std::string fileHeader = config().getString("PageCompiler.fileHeader", ""); + if (!fileHeader.empty()) + { + ostr << fileHeader << std::endl; + ostr << "\n\n"; + } + ostr << "#include \"" << headerFileName << "\"\n"; + ostr << "#include \"Poco/Net/HTTPServerRequest.h\"\n"; + ostr << "#include \"Poco/Net/HTTPServerResponse.h\"\n"; + ostr << "#include \"Poco/Net/HTMLForm.h\"\n"; + + if (_generateOSPCode) + { + if (page.has("page.session")) + { + ostr << "#include \"Poco/OSP/Web/WebSession.h\"\n"; + ostr << "#include \"Poco/OSP/Web/WebSessionManager.h\"\n"; + ostr << "#include \"Poco/OSP/ServiceRegistry.h\"\n"; + } + } + else if (_generateApacheCode) + { + ostr << "#include \"Poco/ClassLibrary.h\"\n"; + } + + ostr << "\n\n"; + + const std::string& decls = page.implDecls(); + if (!decls.empty()) + { + ostr << decls; + ostr << "\n\n"; + } + + beginNamespace(ostr, page); + + Path p(headerFileName); + std::string base(page.get("page.baseClass", "Poco::Net::HTTPRequestHandler")); + std::string ctorArg(page.get("page.ctorArg", "")); + + if (_generateOSPCode) + { + ostr << clazz << "::" << clazz << "(Poco::OSP::BundleContext::Ptr pContext):\n"; + ostr << "\t_pContext(pContext)\n"; + ostr << "{\n}\n"; + ostr << "\n\n"; + } + else if (!ctorArg.empty()) + { + ostr << clazz << "::" << clazz << "(" << ctorArg << " arg):\n"; + ostr << "\t" << base << "(arg)\n"; + ostr << "{\n}\n"; + ostr << "\n\n"; + } + + ostr << "void " << clazz << "::handleRequest(Poco::Net::HTTPServerRequest& request, Poco::Net::HTTPServerResponse& response)\n"; + ostr << "{\n"; + + if (_generateOSPCode && page.has("page.session")) + { + writeOSPSession(ostr, page); + } + + if (page.get("page.form", "true") != "false") + { + std::string partHandler(page.get("page.formPartHandler", "")); + if (!partHandler.empty()) + { + ostr << "\t" << partHandler << " cpspPartHandler(*this);\n"; + } + ostr << "\tPoco::Net::HTMLForm form(request, request.stream()"; + if (!partHandler.empty()) + { + ostr << ", cpspPartHandler"; + } + ostr << ");\n"; + } + + std::string contentType(page.get("page.contentType", "text/html")); + std::string chunked(page.get("page.chunked", "true")); + + if (chunked != "false") + { + ostr << "\tresponse.setChunkedTransferEncoding(true);\n"; + } + + ostr << "\tresponse.setContentType(\"" << contentType << "\");\n"; + ostr << "\n"; + + ostr << "\tstd::ostream& ostr = response.send();\n"; + ostr << page.handler(); + + ostr << "}\n"; + + if (_generateOSPCode) + { + ostr << "\n\n"; + writeFactoryImpl(ostr, page, clazz, "context()"); + } + else if (_generateApacheCode) + { + ostr << "\n\n"; + writeFactoryImpl(ostr, page, clazz, ""); + } + + endNamespace(ostr, page); + + if (_generateApacheCode) + writeManifest(ostr, page, clazz); + } + + void beginNamespace(std::ostream& ostr, Page& page) + { + std::string ns = page.get("page.namespace", ""); + if (!ns.empty()) + { + StringTokenizer tok(ns, ":", StringTokenizer::TOK_IGNORE_EMPTY | StringTokenizer::TOK_TRIM); + for (StringTokenizer::Iterator it = tok.begin(); it != tok.end(); ++it) + { + ostr << "namespace " << *it << " {\n"; + } + ostr << "\n\n"; + } + } + + void endNamespace(std::ostream& ostr, Page& page) + { + std::string ns = page.get("page.namespace", ""); + if (!ns.empty()) + { + ostr << "\n\n"; + StringTokenizer tok(ns, ":", StringTokenizer::TOK_IGNORE_EMPTY | StringTokenizer::TOK_TRIM); + for (StringTokenizer::Iterator it = tok.begin(); it != tok.end(); ++it) + { + ostr << "} "; + } + ostr << "// namespace " << ns << "\n"; + } + } + + void writeOSPSession(std::ostream& ostr, Page& page) + { + ostr << "\tPoco::OSP::Web::WebSession::Ptr session;\n"; + ostr << "\t{\n"; + ostr << "\t\tPoco::OSP::ServiceRef::Ptr pWebSessionManagerRef = context()->registry().findByName(Poco::OSP::Web::WebSessionManager::SERVICE_NAME);\n"; + ostr << "\t\tif (pWebSessionManagerRef)\n"; + ostr << "\t\t{\n"; + ostr << "\t\t\tPoco::OSP::Web::WebSessionManager::Ptr pWebSessionManager = pWebSessionManagerRef->castedInstance();\n"; + ostr << "\t\t\tsession = pWebSessionManager->find(\"" << page.get("page.session") << "\", request);\n"; + ostr << "\t\t\tif (session.isNull())\n"; + ostr << "\t\t\t\tsession = pWebSessionManager->get(\"" << page.get("page.session") << "\", request, " << page.get("page.sessionTimeout", "30") << ", context());\n"; + ostr << "\t\t}\n"; + ostr << "\t}\n"; + } + + void writeOSPHeader(std::ostream& ostr) + { + ostr << "\n"; + ostr << "protected:\n"; + ostr << "\tPoco::OSP::BundleContext::Ptr context() const\n"; + ostr << "\t{\n"; + ostr << "\t\treturn _pContext;\n"; + ostr << "\t}\n"; + ostr << "\n"; + ostr << "private:\n"; + ostr << "\tPoco::OSP::BundleContext::Ptr _pContext;\n"; + } + + void writeFactoryHeader(std::ostream& ostr, Page& page, const std::string& clazz, const std::string& base) + { + ostr << "class " << clazz << "Factory: public " << base << "\n"; + ostr << "{\n"; + ostr << "public:\n"; + ostr << "\tPoco::Net::HTTPRequestHandler* createRequestHandler(const Poco::Net::HTTPServerRequest& request);\n"; + ostr << "};\n"; + } + + void writeFactoryImpl(std::ostream& ostr, Page& page, const std::string& clazz, const std::string& arg) + { + ostr << "Poco::Net::HTTPRequestHandler* " << clazz << "Factory::createRequestHandler(const Poco::Net::HTTPServerRequest& request)\n"; + ostr << "{\n"; + ostr << "\treturn new " << clazz << "(" << arg << ");\n"; + ostr << "}\n"; + } + + void writeManifest(std::ostream& ostr, Page& page, const std::string& clazz) + { + std::string ns = page.get("page.namespace", ""); + if (!ns.empty()) ns += "::"; + ostr << "\n\n"; + ostr << "POCO_BEGIN_MANIFEST(Poco::Net::HTTPRequestHandlerFactory)\n"; + ostr << "\tPOCO_EXPORT_CLASS(" << ns << clazz << "Factory)\n"; + ostr << "POCO_END_MANIFEST\n"; + } + +private: + bool _helpRequested; + bool _generateOSPCode; + bool _generateApacheCode; +}; + + +POCO_APP_MAIN(CompilerApp) diff --git a/PageCompiler/test/header.inc b/PageCompiler/test/header.inc new file mode 100644 index 000000000..02cf0bd9a --- /dev/null +++ b/PageCompiler/test/header.inc @@ -0,0 +1,3 @@ +

This is the header

+

Just to test the include statement

+<%@ foo bar="asdf"%> diff --git a/PageCompiler/test/test.cpsp b/PageCompiler/test/test.cpsp new file mode 100644 index 000000000..055d87168 --- /dev/null +++ b/PageCompiler/test/test.cpsp @@ -0,0 +1,27 @@ +<%@ page class="TestHandler" namespace="AI::Test" session="test"%> +<%! +#include "Poco/DateTime.h" +#include "Poco/DateTimeFormatter.h" +#include "Poco/DateTimeFormat.h" + + +using Poco::DateTime; +using Poco::DateTimeFormatter; +using Poco::DateTimeFormat; +%> + +<% + DateTime now; + std::string dt(DateTimeFormatter::format(now, DateTimeFormat::SORTABLE_FORMAT)); +%> + + +Test Page + +<%-- This is a server-side comment --%> + +<%@ include page="header.inc" %> +

Hello, World!

+

<%= dt %>

+ +