Adding for loop capability
[shibboleth/cpp-xmltooling.git] / xmltooling / util / TemplateEngine.cpp
index 4a66018..22881e3 100644 (file)
@@ -1,6 +1,6 @@
 /*
- *  Copyright 2001-2006 Internet2
- * 
+ *  Copyright 2001-2009 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
 
 /**
  * TemplateEngine.cpp
- * 
- * Simple template replacement engine. 
+ *
+ * Simple template replacement engine.
  */
 
 #include "internal.h"
 #include "util/TemplateEngine.h"
 
-#include <ctime>
-
 using namespace xmltooling;
 using namespace std;
 
@@ -35,8 +33,12 @@ void TemplateEngine::setTagPrefix(const char* tagPrefix)
     ifnottag = string("<") + tagPrefix + "ifnot ";
     ifendtag = string("</") + tagPrefix + "if>";
     ifnotendtag = string("</") + tagPrefix + "ifnot>";
+    fortag = string("<") + tagPrefix + "for ";
+    forendtag = string("</") + tagPrefix + "for>";
 }
 
+string TemplateEngine::unsafe_chars = "#%&():[]\\`{}";
+
 void TemplateEngine::html_encode(ostream& os, const char* start) const
 {
     while (start && *start) {
@@ -44,10 +46,18 @@ void TemplateEngine::html_encode(ostream& os, const char* start) const
             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;
+
+            default:
+                if (unsafe_chars.find_first_of(*start) != string::npos)
+                    os << "&#" << static_cast<short>(*start) << ';';
+                else
+                    os << *start;
+
+            /*
+            case '#':   os << "&#35;";      break;
+            case '%':   os << "&#37;";      break;
             case '(':   os << "&#40;";      break;
             case ')':   os << "&#41;";      break;
             case ':':   os << "&#58;";      break;
@@ -58,6 +68,7 @@ void TemplateEngine::html_encode(ostream& os, const char* start) const
             case '{':   os << "&#123;";     break;
             case '}':   os << "&#125;";     break;
             default:    os << *start;
+            */
         }
         start++;
     }
@@ -82,19 +93,10 @@ void TemplateEngine::process(
     const string& buf,
     const char*& lastpos,
     ostream& os,
-    const map<string,string>& parameters,
+    const TemplateParameters& 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;
 
@@ -102,7 +104,7 @@ void TemplateEngine::process(
         // 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()))
@@ -112,21 +114,17 @@ void TemplateEngine::process(
         {
             // 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);
-                }
+
+                const char* p = parameters.getParameter(key.c_str());
+                if (!p && e)
+                    p = e->getProperty(key.c_str());
+                if (p)
+                    html_encode(os,p);
                 lastpos = thispos + 2; // strlen("/>")
             }
         }
@@ -138,19 +136,14 @@ void TemplateEngine::process(
         {
             // 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;
-                }
+                if (visible)
+                    cond = parameters.getParameter(key.c_str()) || (e && e->getProperty(key.c_str()));
                 lastpos = thispos + 1; // strlen(">")
                 process(cond, buf, lastpos, os, parameters, e);
             }
@@ -173,19 +166,14 @@ void TemplateEngine::process(
         {
             // 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;
-                }
+                if (visible)
+                    cond = !(parameters.getParameter(key.c_str()) || (e && e->getProperty(key.c_str())));
                 lastpos = thispos + 1; // strlen(">")
                 process(cond, buf, lastpos, os, parameters, e);
             }
@@ -200,6 +188,53 @@ void TemplateEngine::process(
             lastpos = thispos + ifnotendtag.length();
             return;
         }
+
+#ifdef HAVE_STRCASECMP
+        else if (!strncasecmp(thispos, fortag.c_str(), fortag.length()))
+#else
+        else if (!_strnicmp(thispos, fortag.c_str(), fortag.length()))
+#endif
+        {
+            // Save this position off.
+            lastpos = thispos + iftag.length();
+            string key;
+            bool cond = visible;
+
+            // search for the end of this tag
+            if ((thispos = strchr(lastpos, '>')) != NULL) {
+                key = buf.substr(lastpos-line, thispos-lastpos);
+                trimspace(key);
+                lastpos = thispos + 1; // strlen(">")
+            }
+            const vector<xmltooling::TemplateEngine::TemplateParameters> forParams = parameters.getParameterCollection(key.c_str());
+
+            unsigned int forend = forParams.size();
+            if (forend==0) {  // have to go through at least once to match end tags
+               cond = false;
+               forend = 1;
+            }
+
+            const char *savlastpos = lastpos;
+            for (unsigned int i=0; i<forend; i++ ) {
+                const TemplateParameters nullp;
+                const TemplateParameters* tp = forParams.size()>0? static_cast<const TemplateParameters*>(&forParams[i]): &nullp;
+                lastpos = savlastpos;
+                process(cond, buf, lastpos, os, *tp, e);
+            }
+
+        }
+
+#ifdef HAVE_STRCASECMP
+        else if (!strncasecmp(thispos, forendtag.c_str(), forendtag.length()))
+#else
+        else if (!_strnicmp(thispos, forendtag.c_str(), forendtag.length()))
+#endif
+        {
+            // Save this position off and pop the stack.
+            lastpos = thispos + forendtag.length();
+            return;
+        }
+
         else {
             // Skip it.
             if (visible)
@@ -211,12 +246,12 @@ void TemplateEngine::process(
         os << buf.substr(lastpos-line);
 }
 
-void TemplateEngine::run(istream& is, ostream& os, const map<string,string>& parameters, const XMLToolingException* e) const
+void TemplateEngine::run(istream& is, ostream& os, const TemplateParameters& 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);
 }