Template replacement engine ported from Shib, added conditional nesting.
authorcantor <cantor@de75baf8-a10c-0410-a50a-987c0e22f00f>
Thu, 19 Oct 2006 03:52:40 +0000 (03:52 +0000)
committercantor <cantor@de75baf8-a10c-0410-a50a-987c0e22f00f>
Thu, 19 Oct 2006 03:52:40 +0000 (03:52 +0000)
git-svn-id: https://svn.middleware.georgetown.edu/cpp-xmltooling/trunk@183 de75baf8-a10c-0410-a50a-987c0e22f00f

xmltooling/Makefile.am
xmltooling/XMLToolingConfig.cpp
xmltooling/XMLToolingConfig.h
xmltooling/util/TemplateEngine.cpp [new file with mode: 0644]
xmltooling/util/TemplateEngine.h [new file with mode: 0644]
xmltooling/xmltooling.vcproj
xmltoolingtest/Makefile.am
xmltoolingtest/TemplateEngineTest.h [new file with mode: 0644]
xmltoolingtest/data/template.in [new file with mode: 0644]
xmltoolingtest/data/template.out [new file with mode: 0644]
xmltoolingtest/xmltoolingtest.vcproj

index 94062fe..240f230 100644 (file)
@@ -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 \
index c2a0749..fac88e1 100644 (file)
@@ -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<void*>::reverse_iterator i=m_libhandles.rbegin(); i!=m_libhandles.rend(); i++) {
 #if defined(WIN32)
index f29f706..f77597f 100644 (file)
@@ -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 (file)
index 0000000..4a66018
--- /dev/null
@@ -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 <ctime>
+
+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("</") + tagPrefix + "if>";
+    ifnotendtag = string("</") + tagPrefix + "ifnot>";
+}
+
+void TemplateEngine::html_encode(ostream& os, const char* start) const
+{
+    while (start && *start) {
+        switch (*start) {
+            case '<':   os << "&lt;";       break;
+            case '>':   os << "&gt;";       break;
+            case '"':   os << "&quot;";     break;
+            case '#':   os << "&#35;";      break;
+            case '%':   os << "&#37;";      break;
+            case '&':   os << "&#38;";      break;
+            case '\'':  os << "&#39;";      break;
+            case '(':   os << "&#40;";      break;
+            case ')':   os << "&#41;";      break;
+            case ':':   os << "&#58;";      break;
+            case '[':   os << "&#91;";      break;
+            case '\\':  os << "&#92;";      break;
+            case ']':   os << "&#93;";      break;
+            case '`':   os << "&#96;";      break;
+            case '{':   os << "&#123;";     break;
+            case '}':   os << "&#125;";     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<string,string>& 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<string,string>::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<string,string>::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<string,string>::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<string,string>& 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 (file)
index 0000000..5183ff7
--- /dev/null
@@ -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 <xmltooling/base.h>
+
+#include <map>
+#include <string>
+#include <iostream>
+
+namespace xmltooling {
+
+    /**
+     * Simple template replacement engine. Supports the following:
+     * <ul>
+     *  <li> &lt;mlp key/&gt; </li>
+     *  <li> &lt;mlpif key&gt; stuff &lt;/mlpif&gt;</li>
+     *  <li> &lt;mlpifnot key&gt; stuff &lt;/mlpifnot&gt;</li>
+     * </ul>
+     * 
+     * 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<std::string,std::string>& 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<std::string,std::string>& parameters,
+            const XMLToolingException* e
+            ) const;
+            
+        std::string keytag,iftag,ifendtag,ifnottag,ifnotendtag;
+    };
+};
+
+#endif /* __xmltooling_template_h__ */
index cd675b6..76dc203 100644 (file)
                                        >\r
                                </File>\r
                                <File\r
+                                       RelativePath=".\util\TemplateEngine.cpp"\r
+                                       >\r
+                               </File>\r
+                               <File\r
                                        RelativePath=".\util\Win32Threads.cpp"\r
                                        >\r
                                </File>\r
                                        >\r
                                </File>\r
                                <File\r
+                                       RelativePath=".\util\TemplateEngine.h"\r
+                                       >\r
+                               </File>\r
+                               <File\r
                                        RelativePath=".\util\Threads.h"\r
                                        >\r
                                </File>\r
index 9430f41..4314674 100644 (file)
@@ -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 (file)
index 0000000..96deea0
--- /dev/null
@@ -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 <fstream>
+#include <sstream>
+#include <xmltooling/util/TemplateEngine.h>
+
+class TemplateEngineTest : public CxxTest::TestSuite {
+public:
+    void setUp() {
+    }
+    
+    void tearDown() {
+    }
+
+    void testTemplateEngine() {
+        auto_ptr<TemplateEngine> engine(new TemplateEngine());
+
+        map<string,string> 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 (file)
index 0000000..2725527
--- /dev/null
@@ -0,0 +1,13 @@
+This is a template containing tags for substitution by the template engine.
+
+<mlp foo1/> <mlp foo2/> <mlp foo3/>
+
+<mlpif foo1><mlp foo3/></mlpif>
+
+<mlpif foo2><mlp foo1/></mlpif>
+
+<mlpif foo1>
+       <mlpifnot foo2>
+               <mlp foo3/>
+       </mlpifnot>
+</mlpif>
diff --git a/xmltoolingtest/data/template.out b/xmltoolingtest/data/template.out
new file mode 100644 (file)
index 0000000..fc8bf75
--- /dev/null
@@ -0,0 +1,13 @@
+This is a template containing tags for substitution by the template engine.
+
+bar1  bar3
+
+bar3
+
+
+
+
+       
+               bar3
+       
+
index 31feac6..d5b8234 100644 (file)
                                >\r
                        </File>\r
                        <File\r
+                               RelativePath=".\TemplateEngineTest.cpp"\r
+                               >\r
+                       </File>\r
+                       <File\r
                                RelativePath=".\UnmarshallingTest.cpp"\r
                                >\r
                        </File>\r
                                        >\r
                                        <Tool\r
                                                Name="VCCustomBuildTool"\r
-                                               CommandLine="\perl\bin\perl.exe -w \cxxtest\cxxtestgen.pl --part --have-eh --have-std --abort-on-fail -o &quot;$(InputName)&quot;.cpp &quot;$(InputPath)&quot;"\r
+                                               CommandLine="\perl\bin\perl.exe -w \cxxtest\cxxtestgen.pl --part --have-eh --have-std --abort-on-fail -o &quot;$(InputName)&quot;.cpp &quot;$(InputPath)&quot;&#x0D;&#x0A;"\r
                                                Outputs="&quot;$(InputName)&quot;.cpp"\r
                                        />\r
                                </FileConfiguration>\r
                                        >\r
                                        <Tool\r
                                                Name="VCCustomBuildTool"\r
-                                               CommandLine="\perl\bin\perl.exe -w \cxxtest\cxxtestgen.pl --part --have-eh --have-std --abort-on-fail -o &quot;$(InputName)&quot;.cpp &quot;$(InputPath)&quot;"\r
+                                               CommandLine="\perl\bin\perl.exe -w \cxxtest\cxxtestgen.pl --part --have-eh --have-std --abort-on-fail -o &quot;$(InputName)&quot;.cpp &quot;$(InputPath)&quot;&#x0D;&#x0A;"\r
                                                Outputs="&quot;$(InputName)&quot;.cpp"\r
                                        />\r
                                </FileConfiguration>\r
                                </FileConfiguration>\r
                        </File>\r
                        <File\r
+                               RelativePath=".\TemplateEngineTest.h"\r
+                               >\r
+                               <FileConfiguration\r
+                                       Name="Debug|Win32"\r
+                                       >\r
+                                       <Tool\r
+                                               Name="VCCustomBuildTool"\r
+                                               CommandLine="\perl\bin\perl.exe -w \cxxtest\cxxtestgen.pl --part --have-eh --have-std --abort-on-fail -o &quot;$(InputName)&quot;.cpp &quot;$(InputPath)&quot;"\r
+                                               Outputs="&quot;$(InputName)&quot;.cpp"\r
+                                       />\r
+                               </FileConfiguration>\r
+                               <FileConfiguration\r
+                                       Name="Release|Win32"\r
+                                       >\r
+                                       <Tool\r
+                                               Name="VCCustomBuildTool"\r
+                                               CommandLine="\perl\bin\perl.exe -w \cxxtest\cxxtestgen.pl --part --have-eh --have-std --abort-on-fail -o &quot;$(InputName)&quot;.cpp &quot;$(InputPath)&quot;"\r
+                                               Outputs="&quot;$(InputName)&quot;.cpp"\r
+                                       />\r
+                               </FileConfiguration>\r
+                       </File>\r
+                       <File\r
                                RelativePath=".\UnmarshallingTest.h"\r
                                >\r
                                <FileConfiguration\r