2 * Copyright 2001-2009 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 "util/TemplateEngine.h"
26 using namespace xmltooling;
29 void TemplateEngine::setTagPrefix(const char* tagPrefix)
31 keytag = string("<") + tagPrefix + " ";
32 iftag = string("<") + tagPrefix + "if ";
33 ifnottag = string("<") + tagPrefix + "ifnot ";
34 ifendtag = string("</") + tagPrefix + "if>";
35 ifnotendtag = string("</") + tagPrefix + "ifnot>";
36 fortag = string("<") + tagPrefix + "for ";
37 forendtag = string("</") + tagPrefix + "for>";
40 string TemplateEngine::unsafe_chars = "#%&():[]\\`{}";
42 void TemplateEngine::html_encode(ostream& os, const char* start) const
44 while (start && *start) {
46 case '<': os << "<"; break;
47 case '>': os << ">"; break;
48 case '"': os << """; break;
49 case '&': os << "&"; break;
50 case '\'': os << "'"; break;
53 if (unsafe_chars.find_first_of(*start) != string::npos)
54 os << "&#" << static_cast<short>(*start) << ';';
59 case '#': os << "#"; break;
60 case '%': os << "%"; break;
61 case '(': os << "("; break;
62 case ')': os << ")"; break;
63 case ':': os << ":"; break;
64 case '[': os << "["; break;
65 case '\\': os << "\"; break;
66 case ']': os << "]"; break;
67 case '`': os << "`"; break;
68 case '{': os << "{"; break;
69 case '}': os << "}"; break;
70 default: os << *start;
77 void TemplateEngine::trimspace(string& s) const
79 string::size_type end = s.size() - 1, start = 0;
81 // Trim stuff on right.
82 while (end > 0 && !isgraph(s[end])) end--;
84 // Trim stuff on left.
85 while (start < end && !isgraph(s[start])) start++;
88 s = s.substr(start, end - start + 1);
91 void TemplateEngine::process(
96 const TemplateParameters& parameters,
97 const XMLToolingException* e
100 const char* line = buf.c_str();
103 while ((thispos = strchr(lastpos, '<')) != NULL) {
104 // Output the string up to this token.
106 os << buf.substr(lastpos-line, thispos-lastpos);
108 // Make sure this token matches our tokens.
109 #ifdef HAVE_STRCASECMP
110 if (visible && !strncasecmp(thispos, keytag.c_str(), keytag.length()))
112 if (visible && !_strnicmp(thispos, keytag.c_str(), keytag.length()))
115 // Save this position off.
116 lastpos = thispos + keytag.length();
118 // search for the end-tag
119 if ((thispos = strstr(lastpos, "/>")) != NULL) {
120 string key = buf.substr(lastpos-line, thispos-lastpos);
123 const char* p = parameters.getParameter(key.c_str());
125 p = e->getProperty(key.c_str());
128 lastpos = thispos + 2; // strlen("/>")
131 #ifdef HAVE_STRCASECMP
132 else if (!strncasecmp(thispos, iftag.c_str(), iftag.length()))
134 else if (!_strnicmp(thispos, iftag.c_str(), iftag.length()))
137 // Save this position off.
138 lastpos = thispos + iftag.length();
140 // search for the end of this tag
141 if ((thispos = strchr(lastpos, '>')) != NULL) {
142 string key = buf.substr(lastpos-line, thispos-lastpos);
146 cond = parameters.getParameter(key.c_str()) || (e && e->getProperty(key.c_str()));
147 lastpos = thispos + 1; // strlen(">")
148 process(cond, buf, lastpos, os, parameters, e);
151 #ifdef HAVE_STRCASECMP
152 else if (!strncasecmp(thispos, ifendtag.c_str(), ifendtag.length()))
154 else if (!_strnicmp(thispos, ifendtag.c_str(), ifendtag.length()))
157 // Save this position off and pop the stack.
158 lastpos = thispos + ifendtag.length();
161 #ifdef HAVE_STRCASECMP
162 else if (!strncasecmp(thispos, ifnottag.c_str(), ifnottag.length()))
164 else if (!_strnicmp(thispos, ifnottag.c_str(), ifnottag.length()))
167 // Save this position off.
168 lastpos = thispos + ifnottag.length();
170 // search for the end of this tag
171 if ((thispos = strchr(lastpos, '>')) != NULL) {
172 string key = buf.substr(lastpos-line, thispos-lastpos);
176 cond = !(parameters.getParameter(key.c_str()) || (e && e->getProperty(key.c_str())));
177 lastpos = thispos + 1; // strlen(">")
178 process(cond, buf, lastpos, os, parameters, e);
181 #ifdef HAVE_STRCASECMP
182 else if (!strncasecmp(thispos, ifnotendtag.c_str(), ifnotendtag.length()))
184 else if (!_strnicmp(thispos, ifnotendtag.c_str(), ifnotendtag.length()))
187 // Save this position off and pop the stack.
188 lastpos = thispos + ifnotendtag.length();
192 #ifdef HAVE_STRCASECMP
193 else if (!strncasecmp(thispos, fortag.c_str(), fortag.length()))
195 else if (!_strnicmp(thispos, fortag.c_str(), fortag.length()))
198 // Save this position off.
199 lastpos = thispos + iftag.length();
203 // search for the end of this tag
204 if ((thispos = strchr(lastpos, '>')) != NULL) {
205 key = buf.substr(lastpos-line, thispos-lastpos);
207 lastpos = thispos + 1; // strlen(">")
210 const vector<TemplateParameters>& forParams = parameters.getParameterCollection(key.c_str());
211 vector<TemplateParameters>::size_type forend = forParams.size();
212 if (forend == 0) { // have to go through at least once to match end tags
217 const char *savlastpos = lastpos;
218 for (vector<TemplateParameters>::size_type i=0; i<forend; ++i) {
219 static TemplateParameters nullp;
220 lastpos = savlastpos;
221 process(cond, buf, lastpos, os, (forParams.size()>0 ? forParams[i] : nullp), e);
226 #ifdef HAVE_STRCASECMP
227 else if (!strncasecmp(thispos, forendtag.c_str(), forendtag.length()))
229 else if (!_strnicmp(thispos, forendtag.c_str(), forendtag.length()))
232 // Save this position off and pop the stack.
233 lastpos = thispos + forendtag.length();
241 lastpos = thispos + 1;
245 os << buf.substr(lastpos-line);
248 void TemplateEngine::run(istream& is, ostream& os, const TemplateParameters& parameters, const XMLToolingException* e) const
251 while (getline(is, line))
254 const char* pos=buf.c_str();
255 process(true, buf, pos, os, parameters, e);