From f6ff54c492df81018cf48da039ee681508f88e46 Mon Sep 17 00:00:00 2001 From: Adrian Kummerlaender Date: Sat, 16 Aug 2014 23:30:36 +0200 Subject: Implemented primitive optional parameter support for external functions * renamed FunctionExternalTextFormatter into FunctionExternalCommand ** the goal is to provide a general interface to a variety of external commands *** e.g. not just text formatters but system utilities for file management and so on ** this requires the stdin parameter to be optional as not all external commands require stdin input * implemented "filter_derived" helper template to determine amount of optional parameters ** optional parameters are defined as "boost::optional" specializations *** they in turn can be detected by checking if "boost::optional_detail::optional_tag" is a base class * "callConstructDocument" member method of "FunctionBase" performs additional bounds checking of parameter vector * "boost::optional" specific member overload was added to "XObjectValue" helper class ** we will have to provide full specializations for all optional types as C++ prohibits partial member function template specialization * renamed the external function in "PlattformGuard" * changed README.md and test cases accordingly --- CMakeLists.txt | 2 +- README.md | 4 +- ixslt.cc | 4 +- src/function/base.h | 27 +++++-- src/function/external_command.cc | 99 +++++++++++++++++++++++ src/function/external_command.h | 31 ++++++++ src/function/external_text_formatter.cc | 101 ------------------------ src/function/external_text_formatter.h | 30 ------- src/platform_guard.cc | 6 +- src/support/filesystem_context.h | 2 +- src/support/include_entity_resolver.cc | 2 +- src/support/include_entity_resolver.h | 2 +- src/support/type/filter.h | 24 ++++++ src/support/type/xobject_value.cc | 13 ++- test/external_text_formatter/reference.xml | 2 +- test/external_text_formatter/transformation.xsl | 2 +- 16 files changed, 200 insertions(+), 151 deletions(-) create mode 100644 src/function/external_command.cc create mode 100644 src/function/external_command.h delete mode 100644 src/function/external_text_formatter.cc delete mode 100644 src/function/external_text_formatter.h create mode 100644 src/support/type/filter.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 9f19bb1..32d8db2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -29,7 +29,7 @@ set( src/function/read_directory.cc src/function/transform.cc src/function/generate.cc - src/function/external_text_formatter.cc + src/function/external_command.cc src/support/filesystem_context.cc src/support/xalan_string.cc src/support/include_entity_resolver.cc diff --git a/README.md b/README.md index 8629b9a..0593bb5 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ Contrary to popular opinion I actually like XSLT as a content transformation lan - external `read-directory` function for read-only directory traversal - external `transform` function for executing transformations inside transformations - external `generate` function for executing transformations and committing the result directly to the filesystem -- external `external-text-formatter` function for executing text formatters and capturing their XML output +- external `external-command` function for executing external commands such as text formatters and capturing their output - external `write-file` function for writing files ## Examples: @@ -24,7 +24,7 @@ The `test` directory contains black-box test cases for every external function p - [`InputXSLT:read-directory`](test/read_directory/transformation.xsl) - [`InputXSLT:transform`](test/transform/transformation.xsl) - [`InputXSLT:generate`](test/generate/transformation.xsl) -- [`InputXSLT:external-text-formatter`](test/external_text_formatter/transformation.xsl) (requires [markdown.pl](http://daringfireball.net/projects/markdown/)) +- [`InputXSLT:external-command` (text formatting)](test/external_text_formatter/transformation.xsl) (requires [markdown.pl](http://daringfireball.net/projects/markdown/)) - [`InputXSLT:write-file`](test/write_file/transformation.xsl) Concepts of how static sites may be generated using InputXSLT are being evaluated in [TestXSLT](https://github.com/KnairdA/TestXSLT). diff --git a/ixslt.cc b/ixslt.cc index 23dc9ca..ff5de25 100644 --- a/ixslt.cc +++ b/ixslt.cc @@ -2,8 +2,8 @@ #include #include -#include "boost/optional.hpp" -#include "boost/program_options.hpp" +#include +#include #include #include diff --git a/src/function/base.h b/src/function/base.h index 2d1e4c4..a8f93b9 100644 --- a/src/function/base.h +++ b/src/function/base.h @@ -5,6 +5,8 @@ #include #include +#include + #include #include @@ -14,6 +16,7 @@ #include "support/include_entity_resolver.h" #include "support/dom/document_cache.h" #include "support/type/sequence.h" +#include "support/type/filter.h" #include "support/type/xobject_value.h" namespace InputXSLT { @@ -23,7 +26,13 @@ template < typename... Types > class FunctionBase : public xalan::Function { - static const std::size_t parameter_count = sizeof...(Types); + static const std::size_t parameter_count = sizeof...(Types); + static const std::size_t optional_parameter_count = std::tuple_size< + typename filter_derived< + boost::optional_detail::optional_tag, + Types... + >::type + >::value; public: FunctionBase(IncludeEntityResolver* resolver): @@ -108,16 +117,20 @@ class FunctionBase : public xalan::Function { valueGetter.get - >::type>(parameters[Index])... + >::type>( + Index < parameters.size() + ? parameters[Index] + : xalan::XObjectPtr() + )... ) ); } inline void validateParameters( - const XObjectArgVectorType& parameters, + const XObjectArgVectorType& parameters, xalan::XPathExecutionContext& executionContext, - xalan::XalanNode* context, - const xalan::Locator* locator + xalan::XalanNode* context, + const xalan::Locator* locator ) const { const bool anyNull = std::any_of( parameters.begin(), @@ -127,7 +140,9 @@ class FunctionBase : public xalan::Function { } ); - if ( parameters.size() != parameter_count || anyNull ) { + if ( parameters.size() > parameter_count || + parameters.size() < optional_parameter_count || + anyNull ) { xalan::XPathExecutionContext::GetAndReleaseCachedString guard( executionContext ); diff --git a/src/function/external_command.cc b/src/function/external_command.cc new file mode 100644 index 0000000..bd2bf84 --- /dev/null +++ b/src/function/external_command.cc @@ -0,0 +1,99 @@ +#include "external_command.h" + +#include + +#include + +#include + +#include + +#include "support/xerces_string_guard.h" +#include "support/dom/result_node_facade.h" + +namespace { + +using InputXSLT::XercesStringGuard; + +inline xercesc::DOMNode* importDocumentElement( + boost::process::pistream& outputStream, + xercesc::DOMDocument* const domDocument +) { + std::stringstream xmlStream( + "" + std::string( + (std::istreambuf_iterator(outputStream)), + (std::istreambuf_iterator()) + ) + "" + ); + + xercesc::XercesDOMParser parser; + parser.parse(xalan::XSLTInputSource(xmlStream)); + + return domDocument->importNode( + parser.getDocument()->getDocumentElement(), + true + ); +} + +} + +namespace InputXSLT { + +DomDocumentCache::document_ptr FunctionExternalCommand::constructDocument( + std::string command, + boost::optional stdinText +) const { + DomDocumentCache::document_ptr domDocument( + DomDocumentCache::createDocument("content") + ); + + boost::process::context context; + context.environment = boost::process::self::get_environment(); + context.stdout_behavior = boost::process::capture_stream(); + context.stdin_behavior = boost::process::capture_stream(); + + boost::process::child commandProcess( + boost::process::launch_shell(command, context) + ); + + if ( stdinText ) { + boost::process::postream& inputStream = commandProcess.get_stdin(); + inputStream << *stdinText; + inputStream.close(); + } + + boost::process::pistream& outputStream = commandProcess.get_stdout(); + boost::process::status status = commandProcess.wait(); + + ResultNodeFacade result(domDocument.get(), "output"); + result.setAttribute("command", command); + result.setAttribute("code", std::to_string(status.exit_status())); + + if ( status.exited() ) { + try { + result.setContent( + importDocumentElement( + outputStream, + domDocument.get() + )->getChildNodes() + ); + + result.setAttribute("result", "success"); + } + catch ( const xercesc::DOMException& exception ) { + result.setAttribute("result", "error"); + + result.setValueNode( + "error", + *XercesStringGuard(exception.msg) + ); + } + } else { + result.setAttribute("result", "error"); + } + + + return domDocument; +} + +} diff --git a/src/function/external_command.h b/src/function/external_command.h new file mode 100644 index 0000000..8af5079 --- /dev/null +++ b/src/function/external_command.h @@ -0,0 +1,31 @@ +#ifndef INPUTXSLT_SRC_FUNCTION_EXTERNAL_COMMAND_H_ +#define INPUTXSLT_SRC_FUNCTION_EXTERNAL_COMMAND_H_ + +#include +#include + +#include "base.h" + +namespace InputXSLT { + +class FunctionExternalCommand : public FunctionBase< + FunctionExternalCommand, + std::string, + boost::optional +> { + public: + using FunctionBase::FunctionBase; + + protected: + friend FunctionBase; + + DomDocumentCache::document_ptr constructDocument( + std::string, + boost::optional + ) const; + +}; + +} + +#endif // INPUTXSLT_SRC_FUNCTION_EXTERNAL_COMMAND_H_ diff --git a/src/function/external_text_formatter.cc b/src/function/external_text_formatter.cc deleted file mode 100644 index 03902e9..0000000 --- a/src/function/external_text_formatter.cc +++ /dev/null @@ -1,101 +0,0 @@ -#include "external_text_formatter.h" - -#include - -#include - -#include - -#include - -#include "support/xerces_string_guard.h" -#include "support/dom/result_node_facade.h" - -namespace { - -using InputXSLT::XercesStringGuard; - -inline xercesc::DOMNode* importDocumentElement( - boost::process::pistream& outputStream, - xercesc::DOMDocument* const domDocument -) { - std::stringstream xmlStream( - "" + std::string( - (std::istreambuf_iterator(outputStream)), - (std::istreambuf_iterator()) - ) + "" - ); - - xercesc::XercesDOMParser parser; - parser.parse(xalan::XSLTInputSource(xmlStream)); - - return domDocument->importNode( - parser.getDocument()->getDocumentElement(), - true - ); -} - -} - -namespace InputXSLT { - -DomDocumentCache::document_ptr FunctionExternalTextFormatter::constructDocument( - std::string formatterCommand, - std::string stdinText -) const { - DomDocumentCache::document_ptr domDocument( - DomDocumentCache::createDocument("content") - ); - - boost::process::context context; - context.environment = boost::process::self::get_environment(); - context.stdout_behavior = boost::process::capture_stream(); - context.stdin_behavior = boost::process::capture_stream(); - - boost::process::child formatterProcess( - boost::process::launch_shell( - formatterCommand, - context - ) - ); - - boost::process::postream& inputStream = formatterProcess.get_stdin(); - boost::process::pistream& outputStream = formatterProcess.get_stdout(); - - inputStream << stdinText; - inputStream.close(); - - boost::process::status status = formatterProcess.wait(); - - ResultNodeFacade result(domDocument.get(), "output"); - result.setAttribute("formatter", formatterCommand); - result.setAttribute("code", std::to_string(status.exit_status())); - - if ( status.exited() ) { - try { - result.setContent( - importDocumentElement( - outputStream, - domDocument.get() - )->getChildNodes() - ); - - result.setAttribute("result", "success"); - } - catch ( const xercesc::DOMException& exception ) { - result.setAttribute("result", "error"); - - result.setValueNode( - "error", - *XercesStringGuard(exception.msg) - ); - } - } else { - result.setAttribute("result", "error"); - } - - - return domDocument; -} - -} diff --git a/src/function/external_text_formatter.h b/src/function/external_text_formatter.h deleted file mode 100644 index 4788784..0000000 --- a/src/function/external_text_formatter.h +++ /dev/null @@ -1,30 +0,0 @@ -#ifndef INPUTXSLT_SRC_FUNCTION_EXTERNAL_TEXT_FORMATTER_H_ -#define INPUTXSLT_SRC_FUNCTION_EXTERNAL_TEXT_FORMATTER_H_ - -#include - -#include "base.h" - -namespace InputXSLT { - -class FunctionExternalTextFormatter : public FunctionBase< - FunctionExternalTextFormatter, - std::string, - std::string -> { - public: - using FunctionBase::FunctionBase; - - protected: - friend FunctionBase; - - DomDocumentCache::document_ptr constructDocument( - std::string, - std::string - ) const; - -}; - -} - -#endif // INPUTXSLT_SRC_FUNCTION_EXTERNAL_TEXT_FORMATTER_H_ diff --git a/src/platform_guard.cc b/src/platform_guard.cc index 6be5b4a..f50d976 100644 --- a/src/platform_guard.cc +++ b/src/platform_guard.cc @@ -11,7 +11,7 @@ #include "function/read_directory.h" #include "function/transform.h" #include "function/generate.h" -#include "function/external_text_formatter.h" +#include "function/external_command.h" namespace InputXSLT { @@ -56,8 +56,8 @@ PlatformGuard::PlatformGuard(const std::vector& path): xalan::XalanTransformer::installExternalFunctionGlobal( customNamespace, - xalan::XalanDOMString("external-text-formatter"), - InputXSLT::FunctionExternalTextFormatter(&this->include_resolver_) + xalan::XalanDOMString("external-command"), + InputXSLT::FunctionExternalCommand(&this->include_resolver_) ); } diff --git a/src/support/filesystem_context.h b/src/support/filesystem_context.h index f3875a3..c00fe29 100644 --- a/src/support/filesystem_context.h +++ b/src/support/filesystem_context.h @@ -4,7 +4,7 @@ #include #include -#include "boost/filesystem.hpp" +#include #include #include diff --git a/src/support/include_entity_resolver.cc b/src/support/include_entity_resolver.cc index deac1d5..197b217 100644 --- a/src/support/include_entity_resolver.cc +++ b/src/support/include_entity_resolver.cc @@ -2,7 +2,7 @@ #include -#include "boost/filesystem.hpp" +#include #include "support/xalan_string.h" #include "support/xerces_string_guard.h" diff --git a/src/support/include_entity_resolver.h b/src/support/include_entity_resolver.h index ea5fb7a..e7c5542 100644 --- a/src/support/include_entity_resolver.h +++ b/src/support/include_entity_resolver.h @@ -4,7 +4,7 @@ #include #include -#include "boost/optional.hpp" +#include #include #include diff --git a/src/support/type/filter.h b/src/support/type/filter.h new file mode 100644 index 0000000..4a0e19f --- /dev/null +++ b/src/support/type/filter.h @@ -0,0 +1,24 @@ +#ifndef INPUTXSLT_SRC_SUPPORT_TYPE_FILTER_H_ +#define INPUTXSLT_SRC_SUPPORT_TYPE_FILTER_H_ + +#include +#include + +namespace InputXSLT { + +template < + typename BaseReference, + typename Head, + typename... Tail +> +struct filter_derived { + typedef typename std::conditional< + std::is_base_of::value, + std::tuple, + std::tuple + >::type type; +}; + +} + +#endif // INPUTXSLT_SRC_SUPPORT_TYPE_FILTER_H_ diff --git a/src/support/type/xobject_value.cc b/src/support/type/xobject_value.cc index 9bb6648..e457a69 100644 --- a/src/support/type/xobject_value.cc +++ b/src/support/type/xobject_value.cc @@ -4,7 +4,8 @@ #include #include -#include "boost/filesystem.hpp" +#include +#include #include @@ -26,6 +27,16 @@ std::string XObjectValue::get( return boost::trim_copy(toString(ptr->str())); } +template <> +boost::optional XObjectValue::get>( + const xalan::XObjectPtr& ptr) const { + if ( ptr.null() ) { + return boost::optional(); + } else { + return this->get(ptr); + } +} + template <> boost::filesystem::path XObjectValue::get( const xalan::XObjectPtr& ptr) const { diff --git a/test/external_text_formatter/reference.xml b/test/external_text_formatter/reference.xml index a98b3d8..b7ae62f 100644 --- a/test/external_text_formatter/reference.xml +++ b/test/external_text_formatter/reference.xml @@ -4,7 +4,7 @@

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.

-

Duis aute irure dolor in reprehenderit in voluptate +

Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.

Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

diff --git a/test/external_text_formatter/transformation.xsl b/test/external_text_formatter/transformation.xsl index aa9a300..c6f11fc 100644 --- a/test/external_text_formatter/transformation.xsl +++ b/test/external_text_formatter/transformation.xsl @@ -16,7 +16,7 @@ - -- cgit v1.2.3