/*
- * 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;
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) {
case '<': os << "<"; break;
case '>': os << ">"; break;
case '"': os << """; break;
- case '#': os << "#"; break;
- case '%': os << "%"; break;
case '&': os << "&"; break;
case '\'': os << "'"; break;
+
+ default:
+ if (unsafe_chars.find_first_of(*start) != string::npos)
+ os << "&#" << static_cast<short>(*start) << ';';
+ else
+ os << *start;
+
+ /*
+ 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++;
}
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;
// 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()))
{
// 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("/>")
}
}
{
// 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);
}
{
// 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);
}
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)
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);
}