From fb770e37f24d038a9b082b8f780edd654501dbd4 Mon Sep 17 00:00:00 2001 From: Scott Cantor Date: Thu, 19 Oct 2006 03:52:40 +0000 Subject: [PATCH] Template replacement engine ported from Shib, added conditional nesting. --- xmltooling/Makefile.am | 2 + xmltooling/XMLToolingConfig.cpp | 10 ++ xmltooling/XMLToolingConfig.h | 22 ++++ xmltooling/util/TemplateEngine.cpp | 222 +++++++++++++++++++++++++++++++++++ xmltooling/util/TemplateEngine.h | 95 +++++++++++++++ xmltooling/xmltooling.vcproj | 8 ++ xmltoolingtest/Makefile.am | 2 + xmltoolingtest/TemplateEngineTest.h | 52 ++++++++ xmltoolingtest/data/template.in | 13 ++ xmltoolingtest/data/template.out | 13 ++ xmltoolingtest/xmltoolingtest.vcproj | 30 ++++- 11 files changed, 467 insertions(+), 2 deletions(-) create mode 100644 xmltooling/util/TemplateEngine.cpp create mode 100644 xmltooling/util/TemplateEngine.h create mode 100644 xmltoolingtest/TemplateEngineTest.h create mode 100644 xmltoolingtest/data/template.in create mode 100644 xmltoolingtest/data/template.out diff --git a/xmltooling/Makefile.am b/xmltooling/Makefile.am index 94062fe..240f230 100644 --- a/xmltooling/Makefile.am +++ b/xmltooling/Makefile.am @@ -81,6 +81,7 @@ utilinclude_HEADERS = \ util/ParserPool.h \ util/ReplayCache.h \ util/StorageService.h \ + util/TemplateEngine.h \ util/Threads.h \ util/XMLConstants.h \ util/XMLHelper.h \ @@ -145,6 +146,7 @@ libxmltooling_la_SOURCES = \ util/ParserPool.cpp \ util/ReplayCache.cpp \ util/StorageService.cpp \ + util/TemplateEngine.cpp \ util/XMLConstants.cpp \ util/XMLHelper.cpp \ validation/ValidatorSuite.cpp \ diff --git a/xmltooling/XMLToolingConfig.cpp b/xmltooling/XMLToolingConfig.cpp index c2a0749..fac88e1 100644 --- a/xmltooling/XMLToolingConfig.cpp +++ b/xmltooling/XMLToolingConfig.cpp @@ -32,6 +32,7 @@ #include "util/NDC.h" #include "util/ReplayCache.h" #include "util/StorageService.h" +#include "util/TemplateEngine.h" #include "util/XMLConstants.h" #include "validation/ValidatorSuite.h" @@ -150,6 +151,12 @@ void XMLToolingConfig::setReplayCache(ReplayCache* replayCache) m_replayCache = replayCache; } +void XMLToolingConfig::setTemplateEngine(TemplateEngine* templateEngine) +{ + delete m_templateEngine; + m_templateEngine = templateEngine; +} + bool XMLToolingInternalConfig::init() { #ifdef _DEBUG @@ -242,6 +249,9 @@ void XMLToolingInternalConfig::term() delete m_replayCache; m_replayCache = NULL; + + delete m_templateEngine; + m_templateEngine = NULL; for (vector::reverse_iterator i=m_libhandles.rbegin(); i!=m_libhandles.rend(); i++) { #if defined(WIN32) diff --git a/xmltooling/XMLToolingConfig.h b/xmltooling/XMLToolingConfig.h index f29f706..f77597f 100644 --- a/xmltooling/XMLToolingConfig.h +++ b/xmltooling/XMLToolingConfig.h @@ -43,6 +43,7 @@ namespace xmltooling { class XMLTOOL_API ReplayCache; class XMLTOOL_API StorageService; + class XMLTOOL_API TemplateEngine; class XMLTOOL_API TrustEngine; class XMLTOOL_API XSECCryptoX509CRL; @@ -61,6 +62,9 @@ namespace xmltooling { /** Global ReplayCache instance. */ ReplayCache* m_replayCache; + + /** Global TemplateEngine instance. */ + TemplateEngine* m_templateEngine; public: virtual ~XMLToolingConfig() {} @@ -150,6 +154,24 @@ namespace xmltooling { ReplayCache* getReplayCache() const { return m_replayCache; } + + /** + * Sets the global TemplateEngine instance. + * This method must be externally synchronized with any code that uses the object. + * Any previously set object is destroyed. + * + * @param templateEngine new TemplateEngine instance to store + */ + void setTemplateEngine(TemplateEngine* templateEngine); + + /** + * Returns the global TemplateEngine instance. + * + * @return global TemplateEngine or NULL + */ + TemplateEngine* getTemplateEngine() const { + return m_templateEngine; + } /** * List of catalog files to load into validating parser pool at initialization time. diff --git a/xmltooling/util/TemplateEngine.cpp b/xmltooling/util/TemplateEngine.cpp new file mode 100644 index 0000000..4a66018 --- /dev/null +++ b/xmltooling/util/TemplateEngine.cpp @@ -0,0 +1,222 @@ +/* + * Copyright 2001-2006 Internet2 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * TemplateEngine.cpp + * + * Simple template replacement engine. + */ + +#include "internal.h" +#include "util/TemplateEngine.h" + +#include + +using namespace xmltooling; +using namespace std; + +void TemplateEngine::setTagPrefix(const char* tagPrefix) +{ + keytag = string("<") + tagPrefix + " "; + iftag = string("<") + tagPrefix + "if "; + ifnottag = string("<") + tagPrefix + "ifnot "; + ifendtag = string(""; + ifnotendtag = string(""; +} + +void TemplateEngine::html_encode(ostream& os, const char* start) const +{ + while (start && *start) { + switch (*start) { + case '<': os << "<"; break; + case '>': os << ">"; break; + case '"': os << """; break; + case '#': os << "#"; break; + case '%': os << "%"; break; + case '&': os << "&"; break; + case '\'': os << "'"; break; + case '(': os << "("; break; + case ')': os << ")"; break; + case ':': os << ":"; break; + case '[': os << "["; break; + case '\\': os << "\"; break; + case ']': os << "]"; break; + case '`': os << "`"; break; + case '{': os << "{"; break; + case '}': os << "}"; break; + default: os << *start; + } + start++; + } +} + +void TemplateEngine::trimspace(string& s) const +{ + string::size_type end = s.size() - 1, start = 0; + + // Trim stuff on right. + while (end > 0 && !isgraph(s[end])) end--; + + // Trim stuff on left. + while (start < end && !isgraph(s[start])) start++; + + // Modify the string. + s = s.substr(start, end - start + 1); +} + +void TemplateEngine::process( + bool visible, + const string& buf, + const char*& lastpos, + ostream& os, + const map& parameters, + const XMLToolingException* e + ) const +{ + // Create a timestamp + time_t now = time(NULL); +#ifdef HAVE_CTIME_R + char nowbuf[32]; + ctime_r(&now); +#else + const char* nowbuf = ctime(&now); +#endif + + const char* line = buf.c_str(); + const char* thispos; + + while ((thispos = strchr(lastpos, '<')) != NULL) { + // Output the string up to this token. + if (visible) + os << buf.substr(lastpos-line, thispos-lastpos); + + // Make sure this token matches our tokens. +#ifdef HAVE_STRCASECMP + if (visible && !strncasecmp(thispos, keytag.c_str(), keytag.length())) +#else + if (visible && !_strnicmp(thispos, keytag.c_str(), keytag.length())) +#endif + { + // Save this position off. + lastpos = thispos + keytag.length(); + + // search for the end-tag + if ((thispos = strstr(lastpos, "/>")) != NULL) { + string key = buf.substr(lastpos-line, thispos-lastpos); + trimspace(key); + + map::const_iterator i=parameters.find(key); + if (i != parameters.end()) { + html_encode(os,i->second.c_str()); + } + else if (e) { + const char* ep = e->getProperty(key.c_str()); + if (ep) + html_encode(os,ep); + } + lastpos = thispos + 2; // strlen("/>") + } + } +#ifdef HAVE_STRCASECMP + else if (!strncasecmp(thispos, iftag.c_str(), iftag.length())) +#else + else if (!_strnicmp(thispos, iftag.c_str(), iftag.length())) +#endif + { + // Save this position off. + lastpos = thispos + iftag.length(); + + // search for the end of this tag + if ((thispos = strchr(lastpos, '>')) != NULL) { + string key = buf.substr(lastpos-line, thispos-lastpos); + trimspace(key); + map::const_iterator i=parameters.find(key); + bool cond=false; + if (visible) { + if (i != parameters.end()) + cond=true; + else if (e && e->getProperty(key.c_str())) + cond=true; + } + lastpos = thispos + 1; // strlen(">") + process(cond, buf, lastpos, os, parameters, e); + } + } +#ifdef HAVE_STRCASECMP + else if (!strncasecmp(thispos, ifendtag.c_str(), ifendtag.length())) +#else + else if (!_strnicmp(thispos, ifendtag.c_str(), ifendtag.length())) +#endif + { + // Save this position off and pop the stack. + lastpos = thispos + ifendtag.length(); + return; + } +#ifdef HAVE_STRCASECMP + else if (!strncasecmp(thispos, ifnottag.c_str(), ifnottag.length())) +#else + else if (!_strnicmp(thispos, ifnottag.c_str(), ifnottag.length())) +#endif + { + // Save this position off. + lastpos = thispos + ifnottag.length(); + + // search for the end of this tag + if ((thispos = strchr(lastpos, '>')) != NULL) { + string key = buf.substr(lastpos-line, thispos-lastpos); + trimspace(key); + map::const_iterator i=parameters.find(key); + bool cond=visible; + if (visible) { + if (i != parameters.end()) + cond=false; + else if (e && e->getProperty(key.c_str())) + cond=false; + } + lastpos = thispos + 1; // strlen(">") + process(cond, buf, lastpos, os, parameters, e); + } + } +#ifdef HAVE_STRCASECMP + else if (!strncasecmp(thispos, ifnotendtag.c_str(), ifnotendtag.length())) +#else + else if (!_strnicmp(thispos, ifnotendtag.c_str(), ifnotendtag.length())) +#endif + { + // Save this position off and pop the stack. + lastpos = thispos + ifnotendtag.length(); + return; + } + else { + // Skip it. + if (visible) + os << '<'; + lastpos = thispos + 1; + } + } + if (visible) + os << buf.substr(lastpos-line); +} + +void TemplateEngine::run(istream& is, ostream& os, const map& parameters, const XMLToolingException* e) const +{ + string buf,line; + while (getline(is, line)) + buf += line + '\n'; + + const char* pos=buf.c_str(); + process(true, buf, pos, os, parameters, e); +} diff --git a/xmltooling/util/TemplateEngine.h b/xmltooling/util/TemplateEngine.h new file mode 100644 index 0000000..5183ff7 --- /dev/null +++ b/xmltooling/util/TemplateEngine.h @@ -0,0 +1,95 @@ +/* + * Copyright 2001-2006 Internet2 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file xmltooling/util/TemplateEngine.h + * + * Simple template replacement engine. + */ + +#ifndef __xmltooling_template_h__ +#define __xmltooling_template_h__ + +#include + +#include +#include +#include + +namespace xmltooling { + + /** + * Simple template replacement engine. Supports the following: + *
    + *
  • <mlp key/>
  • + *
  • <mlpif key> stuff </mlpif>
  • + *
  • <mlpifnot key> stuff </mlpifnot>
  • + *
+ * + * The default tag prefix is "mlp". This can be overridden for + * compatibility. + */ + class XMLTOOL_API TemplateEngine + { + MAKE_NONCOPYABLE(TemplateEngine); + public: + + TemplateEngine() { + setTagPrefix("mlp"); + } + + virtual ~TemplateEngine() {} + + /** + * Sets the tag name to use when locating template replacement tags. + * + * @param tagPrefix base prefix for tags + */ + void setTagPrefix(const char* tagPrefix); + + /** + * Processes template from an input stream and executes replacements and + * conditional logic based on parameters. + * + * @param is input stream providing template + * @param os output stream to send results of executing template + * @param parameters name/value parameters to plug into template + * @param e optional exception to extract parameters from + */ + virtual void run( + std::istream& is, + std::ostream& os, + const std::map& parameters, + const XMLToolingException* e=NULL + ) const; + + private: + void trimspace(std::string& s) const; + void html_encode(std::ostream& os, const char* start) const; + void process( + bool visible, + const std::string& buf, + const char*& lastpos, + std::ostream& os, + const std::map& parameters, + const XMLToolingException* e + ) const; + + std::string keytag,iftag,ifendtag,ifnottag,ifnotendtag; + }; +}; + +#endif /* __xmltooling_template_h__ */ diff --git a/xmltooling/xmltooling.vcproj b/xmltooling/xmltooling.vcproj index cd675b6..76dc203 100644 --- a/xmltooling/xmltooling.vcproj +++ b/xmltooling/xmltooling.vcproj @@ -249,6 +249,10 @@ > + + @@ -523,6 +527,10 @@ > + + diff --git a/xmltoolingtest/Makefile.am b/xmltoolingtest/Makefile.am index 9430f41..4314674 100644 --- a/xmltoolingtest/Makefile.am +++ b/xmltoolingtest/Makefile.am @@ -22,7 +22,9 @@ xmltoolingtest_h = \ ExceptionTest.h \ KeyInfoTest.h \ MarshallingTest.h \ + MemoryStorageServiceTest.h \ UnmarshallingTest.h \ + TemplateEngineTest.h \ xmltoolingtest.h \ ${xmlsec_sources} diff --git a/xmltoolingtest/TemplateEngineTest.h b/xmltoolingtest/TemplateEngineTest.h new file mode 100644 index 0000000..96deea0 --- /dev/null +++ b/xmltoolingtest/TemplateEngineTest.h @@ -0,0 +1,52 @@ +/* + * Copyright 2001-2006 Internet2 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "XMLObjectBaseTestCase.h" + +#include +#include +#include + +class TemplateEngineTest : public CxxTest::TestSuite { +public: + void setUp() { + } + + void tearDown() { + } + + void testTemplateEngine() { + auto_ptr engine(new TemplateEngine()); + + map p; + p["foo1"] = "bar1"; + p["foo3"] = "bar3"; + + string path = data_path + "template.in"; + ifstream in(path.c_str()); + ostringstream out; + + engine->run(in,out,p); + + string compstr; + path = data_path + "template.out"; + ifstream compfile(path.c_str()); + while (getline(compfile,path)) + compstr += path + '\n'; + + TSM_ASSERT_EQUALS("Template output did not match.", out.str(), compstr); + } +}; diff --git a/xmltoolingtest/data/template.in b/xmltoolingtest/data/template.in new file mode 100644 index 0000000..2725527 --- /dev/null +++ b/xmltoolingtest/data/template.in @@ -0,0 +1,13 @@ +This is a template containing tags for substitution by the template engine. + + + + + + + + + + + + diff --git a/xmltoolingtest/data/template.out b/xmltoolingtest/data/template.out new file mode 100644 index 0000000..fc8bf75 --- /dev/null +++ b/xmltoolingtest/data/template.out @@ -0,0 +1,13 @@ +This is a template containing tags for substitution by the template engine. + +bar1 bar3 + +bar3 + + + + + + bar3 + + diff --git a/xmltoolingtest/xmltoolingtest.vcproj b/xmltoolingtest/xmltoolingtest.vcproj index 31feac6..d5b8234 100644 --- a/xmltoolingtest/xmltoolingtest.vcproj +++ b/xmltoolingtest/xmltoolingtest.vcproj @@ -216,6 +216,10 @@ > + + @@ -391,7 +395,7 @@ > @@ -400,7 +404,7 @@ > @@ -428,6 +432,28 @@ + + + + + + + +