2 * Copyright 2001-2010 Internet2
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
20 * Simple template replacement engine.
24 #include "io/GenericRequest.h"
25 #include "util/TemplateEngine.h"
27 using namespace xmltooling;
31 static const pair<const string,string> emptyPair;
34 TemplateEngine::TemplateEngine()
39 TemplateEngine::~TemplateEngine()
43 TemplateEngine::TemplateParameters::TemplateParameters() : m_request(nullptr)
47 TemplateEngine::TemplateParameters::~TemplateParameters()
51 const char* TemplateEngine::TemplateParameters::getParameter(const char* name) const
53 map<string,string>::const_iterator i=m_map.find(name);
54 return (i!=m_map.end() ? i->second.c_str() : (m_request ? m_request->getParameter(name) : nullptr));
57 const multimap<string,string>* TemplateEngine::TemplateParameters::getLoopCollection(const char* name) const
59 map< string,multimap<string,string> >::const_iterator i=m_collectionMap.find(name);
60 return (i!=m_collectionMap.end() ? &(i->second) : nullptr);
63 void TemplateEngine::setTagPrefix(const char* tagPrefix)
65 keytag = string("<") + tagPrefix + " ";
66 iftag = string("<") + tagPrefix + "if ";
67 ifnottag = string("<") + tagPrefix + "ifnot ";
68 ifendtag = string("</") + tagPrefix + "if>";
69 ifnotendtag = string("</") + tagPrefix + "ifnot>";
70 fortag = string("<") + tagPrefix + "for ";
71 forendtag = string("</") + tagPrefix + "for>";
74 string TemplateEngine::unsafe_chars = "#%&():[]\\`{}";
76 void TemplateEngine::html_encode(ostream& os, const char* start) const
78 while (start && *start) {
80 case '<': os << "<"; break;
81 case '>': os << ">"; break;
82 case '"': os << """; break;
83 case '&': os << "&"; break;
84 case '\'': os << "'"; break;
87 if (unsafe_chars.find_first_of(*start) != string::npos)
88 os << "&#" << static_cast<short>(*start) << ';';
93 case '#': os << "#"; break;
94 case '%': os << "%"; break;
95 case '(': os << "("; break;
96 case ')': os << ")"; break;
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 default: os << *start;
111 void TemplateEngine::trimspace(string& s) const
113 string::size_type end = s.size() - 1, start = 0;
115 // Trim stuff on right.
116 while (end > 0 && !isgraph(s[end])) end--;
118 // Trim stuff on left.
119 while (start < end && !isgraph(s[start])) start++;
121 // Modify the string.
122 s = s.substr(start, end - start + 1);
125 void TemplateEngine::process(
128 const char*& lastpos,
130 const TemplateParameters& parameters,
131 const std::pair<const std::string,std::string>& loopentry,
132 const XMLToolingException* e
135 const char* line = buf.c_str();
138 while ((thispos = strchr(lastpos, '<')) != nullptr) {
139 // Output the string up to this token.
141 os << buf.substr(lastpos-line, thispos-lastpos);
143 // Make sure this token matches our tokens.
144 #ifdef HAVE_STRCASECMP
145 if (visible && !strncasecmp(thispos, keytag.c_str(), keytag.length()))
147 if (visible && !_strnicmp(thispos, keytag.c_str(), keytag.length()))
150 // Save this position off.
151 lastpos = thispos + keytag.length();
153 // search for the end-tag
154 if ((thispos = strstr(lastpos, "/>")) != nullptr) {
155 string key = buf.substr(lastpos-line, thispos-lastpos);
158 if (key == "$name" && !loopentry.first.empty())
159 html_encode(os,loopentry.first.c_str());
160 else if (key == "$value" && !loopentry.second.empty())
161 html_encode(os,loopentry.second.c_str());
163 const char* p = parameters.getParameter(key.c_str());
165 p = e->getProperty(key.c_str());
169 lastpos = thispos + 2; // strlen("/>")
172 #ifdef HAVE_STRCASECMP
173 else if (!strncasecmp(thispos, iftag.c_str(), iftag.length()))
175 else if (!_strnicmp(thispos, iftag.c_str(), iftag.length()))
178 // Save this position off.
179 lastpos = thispos + iftag.length();
181 // search for the end of this tag
182 if ((thispos = strchr(lastpos, '>')) != nullptr) {
183 string key = buf.substr(lastpos-line, thispos-lastpos);
187 cond = parameters.getParameter(key.c_str()) || (e && e->getProperty(key.c_str()));
188 lastpos = thispos + 1; // strlen(">")
189 process(cond, buf, lastpos, os, parameters, loopentry, e);
192 #ifdef HAVE_STRCASECMP
193 else if (!strncasecmp(thispos, ifendtag.c_str(), ifendtag.length()))
195 else if (!_strnicmp(thispos, ifendtag.c_str(), ifendtag.length()))
198 // Save this position off and pop the stack.
199 lastpos = thispos + ifendtag.length();
202 #ifdef HAVE_STRCASECMP
203 else if (!strncasecmp(thispos, ifnottag.c_str(), ifnottag.length()))
205 else if (!_strnicmp(thispos, ifnottag.c_str(), ifnottag.length()))
208 // Save this position off.
209 lastpos = thispos + ifnottag.length();
211 // search for the end of this tag
212 if ((thispos = strchr(lastpos, '>')) != nullptr) {
213 string key = buf.substr(lastpos-line, thispos-lastpos);
217 cond = !(parameters.getParameter(key.c_str()) || (e && e->getProperty(key.c_str())));
218 lastpos = thispos + 1; // strlen(">")
219 process(cond, buf, lastpos, os, parameters, loopentry, e);
222 #ifdef HAVE_STRCASECMP
223 else if (!strncasecmp(thispos, ifnotendtag.c_str(), ifnotendtag.length()))
225 else if (!_strnicmp(thispos, ifnotendtag.c_str(), ifnotendtag.length()))
228 // Save this position off and pop the stack.
229 lastpos = thispos + ifnotendtag.length();
233 #ifdef HAVE_STRCASECMP
234 else if (!strncasecmp(thispos, fortag.c_str(), fortag.length()))
236 else if (!_strnicmp(thispos, fortag.c_str(), fortag.length()))
239 // Save this position off.
240 lastpos = thispos + iftag.length();
244 // search for the end of this tag
245 if ((thispos = strchr(lastpos, '>')) != nullptr) {
246 key = buf.substr(lastpos-line, thispos-lastpos);
248 lastpos = thispos + 1; // strlen(">")
251 const multimap<string,string>* forParams = parameters.getLoopCollection(key.c_str());
252 if (!forParams || forParams->size() == 0) {
253 process(false, buf, lastpos, os, parameters, emptyPair, e);
256 const char* savlastpos = lastpos;
257 for (multimap<string,string>::const_iterator i=forParams->begin(); i!=forParams->end(); ++i) {
258 lastpos = savlastpos;
259 process(cond, buf, lastpos, os, parameters, *i, e);
264 #ifdef HAVE_STRCASECMP
265 else if (!strncasecmp(thispos, forendtag.c_str(), forendtag.length()))
267 else if (!_strnicmp(thispos, forendtag.c_str(), forendtag.length()))
270 // Save this position off and pop the stack.
271 lastpos = thispos + forendtag.length();
279 lastpos = thispos + 1;
283 os << buf.substr(lastpos-line);
286 void TemplateEngine::run(istream& is, ostream& os, const TemplateParameters& parameters, const XMLToolingException* e) const
289 while (getline(is, line))
292 const char* pos=buf.c_str();
293 process(true, buf, pos, os, parameters, emptyPair, e);