2 * Licensed to the University Corporation for Advanced Internet
3 * Development, Inc. (UCAID) under one or more contributor license
4 * agreements. See the NOTICE file distributed with this work for
5 * additional information regarding copyright ownership.
7 * UCAID licenses this file to you under the Apache License,
8 * Version 2.0 (the "License"); you may not use this file except
9 * in compliance with the License. You may obtain a copy of the
12 * http://www.apache.org/licenses/LICENSE-2.0
14 * Unless required by applicable law or agreed to in writing,
15 * software distributed under the License is distributed on an
16 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
17 * either express or implied. See the License for the specific
18 * language governing permissions and limitations under the License.
24 * Simple template replacement engine.
28 #include "io/GenericRequest.h"
29 #include "util/TemplateEngine.h"
31 using namespace xmltooling;
35 static const pair<const string,string> emptyPair;
38 TemplateEngine::TemplateEngine()
43 TemplateEngine::~TemplateEngine()
47 TemplateEngine::TemplateParameters::TemplateParameters() : m_request(nullptr)
51 TemplateEngine::TemplateParameters::~TemplateParameters()
55 const char* TemplateEngine::TemplateParameters::getParameter(const char* name) const
57 map<string,string>::const_iterator i=m_map.find(name);
58 return (i!=m_map.end() ? i->second.c_str() : (m_request ? m_request->getParameter(name) : nullptr));
61 const multimap<string,string>* TemplateEngine::TemplateParameters::getLoopCollection(const char* name) const
63 map< string,multimap<string,string> >::const_iterator i=m_collectionMap.find(name);
64 return (i!=m_collectionMap.end() ? &(i->second) : nullptr);
67 void TemplateEngine::setTagPrefix(const char* tagPrefix)
69 keytag = string("<") + tagPrefix + " ";
70 iftag = string("<") + tagPrefix + "if ";
71 ifnottag = string("<") + tagPrefix + "ifnot ";
72 ifendtag = string("</") + tagPrefix + "if>";
73 ifnotendtag = string("</") + tagPrefix + "ifnot>";
74 fortag = string("<") + tagPrefix + "for ";
75 forendtag = string("</") + tagPrefix + "for>";
78 string TemplateEngine::unsafe_chars = "#%&():[]\\`{}";
80 void TemplateEngine::html_encode(ostream& os, const char* start) const
82 while (start && *start) {
84 case '<': os << "<"; break;
85 case '>': os << ">"; break;
86 case '"': os << """; break;
87 case '&': os << "&"; break;
88 case '\'': os << "'"; break;
91 if (unsafe_chars.find_first_of(*start) != string::npos)
92 os << "&#" << static_cast<short>(*start) << ';';
97 case '#': os << "#"; break;
98 case '%': os << "%"; break;
99 case '(': os << "("; break;
100 case ')': os << ")"; break;
101 case ':': os << ":"; break;
102 case '[': os << "["; break;
103 case '\\': os << "\"; break;
104 case ']': os << "]"; break;
105 case '`': os << "`"; break;
106 case '{': os << "{"; break;
107 case '}': os << "}"; break;
108 default: os << *start;
115 void TemplateEngine::trimspace(string& s) const
117 string::size_type end = s.size() - 1, start = 0;
119 // Trim stuff on right.
120 while (end > 0 && !isgraph(s[end])) end--;
122 // Trim stuff on left.
123 while (start < end && !isgraph(s[start])) start++;
125 // Modify the string.
126 s = s.substr(start, end - start + 1);
129 void TemplateEngine::process(
132 const char*& lastpos,
134 const TemplateParameters& parameters,
135 const std::pair<const std::string,std::string>& loopentry,
136 const XMLToolingException* e
139 const char* line = buf.c_str();
142 while ((thispos = strchr(lastpos, '<')) != nullptr) {
143 // Output the string up to this token.
145 os << buf.substr(lastpos-line, thispos-lastpos);
147 // Make sure this token matches our tokens.
148 #ifdef HAVE_STRCASECMP
149 if (visible && !strncasecmp(thispos, keytag.c_str(), keytag.length()))
151 if (visible && !_strnicmp(thispos, keytag.c_str(), keytag.length()))
154 // Save this position off.
155 lastpos = thispos + keytag.length();
157 // search for the end-tag
158 if ((thispos = strstr(lastpos, "/>")) != nullptr) {
159 string key = buf.substr(lastpos-line, thispos-lastpos);
162 if (key == "$name" && !loopentry.first.empty())
163 html_encode(os,loopentry.first.c_str());
164 else if (key == "$value" && !loopentry.second.empty())
165 html_encode(os,loopentry.second.c_str());
167 const char* p = parameters.getParameter(key.c_str());
169 p = e->getProperty(key.c_str());
173 lastpos = thispos + 2; // strlen("/>")
176 #ifdef HAVE_STRCASECMP
177 else if (!strncasecmp(thispos, iftag.c_str(), iftag.length()))
179 else if (!_strnicmp(thispos, iftag.c_str(), iftag.length()))
182 // Save this position off.
183 lastpos = thispos + iftag.length();
185 // search for the end of this tag
186 if ((thispos = strchr(lastpos, '>')) != nullptr) {
187 string key = buf.substr(lastpos-line, thispos-lastpos);
191 cond = parameters.getParameter(key.c_str()) || (e && e->getProperty(key.c_str()));
192 lastpos = thispos + 1; // strlen(">")
193 process(cond, buf, lastpos, os, parameters, loopentry, e);
196 #ifdef HAVE_STRCASECMP
197 else if (!strncasecmp(thispos, ifendtag.c_str(), ifendtag.length()))
199 else if (!_strnicmp(thispos, ifendtag.c_str(), ifendtag.length()))
202 // Save this position off and pop the stack.
203 lastpos = thispos + ifendtag.length();
206 #ifdef HAVE_STRCASECMP
207 else if (!strncasecmp(thispos, ifnottag.c_str(), ifnottag.length()))
209 else if (!_strnicmp(thispos, ifnottag.c_str(), ifnottag.length()))
212 // Save this position off.
213 lastpos = thispos + ifnottag.length();
215 // search for the end of this tag
216 if ((thispos = strchr(lastpos, '>')) != nullptr) {
217 string key = buf.substr(lastpos-line, thispos-lastpos);
221 cond = !(parameters.getParameter(key.c_str()) || (e && e->getProperty(key.c_str())));
222 lastpos = thispos + 1; // strlen(">")
223 process(cond, buf, lastpos, os, parameters, loopentry, e);
226 #ifdef HAVE_STRCASECMP
227 else if (!strncasecmp(thispos, ifnotendtag.c_str(), ifnotendtag.length()))
229 else if (!_strnicmp(thispos, ifnotendtag.c_str(), ifnotendtag.length()))
232 // Save this position off and pop the stack.
233 lastpos = thispos + ifnotendtag.length();
237 #ifdef HAVE_STRCASECMP
238 else if (!strncasecmp(thispos, fortag.c_str(), fortag.length()))
240 else if (!_strnicmp(thispos, fortag.c_str(), fortag.length()))
243 // Save this position off.
244 lastpos = thispos + iftag.length();
248 // search for the end of this tag
249 if ((thispos = strchr(lastpos, '>')) != nullptr) {
250 key = buf.substr(lastpos-line, thispos-lastpos);
252 lastpos = thispos + 1; // strlen(">")
255 const multimap<string,string>* forParams = parameters.getLoopCollection(key.c_str());
256 if (!forParams || forParams->size() == 0) {
257 process(false, buf, lastpos, os, parameters, emptyPair, e);
260 const char* savlastpos = lastpos;
261 for (multimap<string,string>::const_iterator i=forParams->begin(); i!=forParams->end(); ++i) {
262 lastpos = savlastpos;
263 process(cond, buf, lastpos, os, parameters, *i, e);
268 #ifdef HAVE_STRCASECMP
269 else if (!strncasecmp(thispos, forendtag.c_str(), forendtag.length()))
271 else if (!_strnicmp(thispos, forendtag.c_str(), forendtag.length()))
274 // Save this position off and pop the stack.
275 lastpos = thispos + forendtag.length();
283 lastpos = thispos + 1;
287 os << buf.substr(lastpos-line);
290 void TemplateEngine::run(istream& is, ostream& os, const TemplateParameters& parameters, const XMLToolingException* e) const
293 while (getline(is, line))
296 const char* pos=buf.c_str();
297 process(true, buf, pos, os, parameters, emptyPair, e);