Cleaned up source for dual build
[shibboleth/cpp-xmltooling.git] / xmltooling / util / TemplateEngine.cpp
1 /*
2  *  Copyright 2001-2007 Internet2
3  * 
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
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 /**
18  * TemplateEngine.cpp
19  * 
20  * Simple template replacement engine. 
21  */
22
23 #include "internal.h"
24 #include "util/TemplateEngine.h"
25
26 using namespace xmltooling;
27 using namespace std;
28
29 void TemplateEngine::setTagPrefix(const char* tagPrefix)
30 {
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 }
37
38 void TemplateEngine::html_encode(ostream& os, const char* start) const
39 {
40     while (start && *start) {
41         switch (*start) {
42             case '<':   os << "&lt;";       break;
43             case '>':   os << "&gt;";       break;
44             case '"':   os << "&quot;";     break;
45             case '#':   os << "&#35;";      break;
46             case '%':   os << "&#37;";      break;
47             case '&':   os << "&#38;";      break;
48             case '\'':  os << "&#39;";      break;
49             case '(':   os << "&#40;";      break;
50             case ')':   os << "&#41;";      break;
51             case ':':   os << "&#58;";      break;
52             case '[':   os << "&#91;";      break;
53             case '\\':  os << "&#92;";      break;
54             case ']':   os << "&#93;";      break;
55             case '`':   os << "&#96;";      break;
56             case '{':   os << "&#123;";     break;
57             case '}':   os << "&#125;";     break;
58             default:    os << *start;
59         }
60         start++;
61     }
62 }
63
64 void TemplateEngine::trimspace(string& s) const
65 {
66   string::size_type end = s.size() - 1, start = 0;
67
68   // Trim stuff on right.
69   while (end > 0 && !isgraph(s[end])) end--;
70
71   // Trim stuff on left.
72   while (start < end && !isgraph(s[start])) start++;
73
74   // Modify the string.
75   s = s.substr(start, end - start + 1);
76 }
77
78 void TemplateEngine::process(
79     bool visible,
80     const string& buf,
81     const char*& lastpos,
82     ostream& os,
83     const TemplateParameters& parameters,
84     const XMLToolingException* e
85     ) const
86 {
87     const char* line = buf.c_str();
88     const char* thispos;
89
90     while ((thispos = strchr(lastpos, '<')) != NULL) {
91         // Output the string up to this token.
92         if (visible)
93             os << buf.substr(lastpos-line, thispos-lastpos);
94     
95         // Make sure this token matches our tokens.
96 #ifdef HAVE_STRCASECMP
97         if (visible && !strncasecmp(thispos, keytag.c_str(), keytag.length()))
98 #else
99         if (visible && !_strnicmp(thispos, keytag.c_str(), keytag.length()))
100 #endif
101         {
102             // Save this position off.
103             lastpos = thispos + keytag.length();
104         
105             // search for the end-tag
106             if ((thispos = strstr(lastpos, "/>")) != NULL) {
107                 string key = buf.substr(lastpos-line, thispos-lastpos);
108                 trimspace(key);
109         
110                 const char* p = parameters.getParameter(key.c_str());
111                 if (!p && e)
112                     p = e->getProperty(key.c_str());
113                 if (p)
114                     html_encode(os,p);
115                 lastpos = thispos + 2; // strlen("/>")
116             }
117         }
118 #ifdef HAVE_STRCASECMP
119         else if (!strncasecmp(thispos, iftag.c_str(), iftag.length()))
120 #else
121         else if (!_strnicmp(thispos, iftag.c_str(), iftag.length()))
122 #endif
123         {
124             // Save this position off.
125             lastpos = thispos + iftag.length();
126     
127             // search for the end of this tag
128             if ((thispos = strchr(lastpos, '>')) != NULL) {
129                 string key = buf.substr(lastpos-line, thispos-lastpos);
130                 trimspace(key);
131                 bool cond=false;
132                 if (visible)
133                     cond = parameters.getParameter(key.c_str()) || (e && e->getProperty(key.c_str()));
134                 lastpos = thispos + 1; // strlen(">")
135                 process(cond, buf, lastpos, os, parameters, e);
136             }
137         }
138 #ifdef HAVE_STRCASECMP
139         else if (!strncasecmp(thispos, ifendtag.c_str(), ifendtag.length()))
140 #else
141         else if (!_strnicmp(thispos, ifendtag.c_str(), ifendtag.length()))
142 #endif
143         {
144             // Save this position off and pop the stack.
145             lastpos = thispos + ifendtag.length();
146             return;
147         }
148 #ifdef HAVE_STRCASECMP
149         else if (!strncasecmp(thispos, ifnottag.c_str(), ifnottag.length()))
150 #else
151         else if (!_strnicmp(thispos, ifnottag.c_str(), ifnottag.length()))
152 #endif
153         {
154             // Save this position off.
155             lastpos = thispos + ifnottag.length();
156     
157             // search for the end of this tag
158             if ((thispos = strchr(lastpos, '>')) != NULL) {
159                 string key = buf.substr(lastpos-line, thispos-lastpos);
160                 trimspace(key);
161                 bool cond=visible;
162                 if (visible)
163                     cond = !(parameters.getParameter(key.c_str()) || (e && e->getProperty(key.c_str())));
164                 lastpos = thispos + 1; // strlen(">")
165                 process(cond, buf, lastpos, os, parameters, e);
166             }
167         }
168 #ifdef HAVE_STRCASECMP
169         else if (!strncasecmp(thispos, ifnotendtag.c_str(), ifnotendtag.length()))
170 #else
171         else if (!_strnicmp(thispos, ifnotendtag.c_str(), ifnotendtag.length()))
172 #endif
173         {
174             // Save this position off and pop the stack.
175             lastpos = thispos + ifnotendtag.length();
176             return;
177         }
178         else {
179             // Skip it.
180             if (visible)
181                 os << '<';
182             lastpos = thispos + 1;
183         }
184     }
185     if (visible)
186         os << buf.substr(lastpos-line);
187 }
188
189 void TemplateEngine::run(istream& is, ostream& os, const TemplateParameters& parameters, const XMLToolingException* e) const
190 {
191     string buf,line;
192     while (getline(is, line))
193         buf += line + '\n';
194     
195     const char* pos=buf.c_str();
196     process(true, buf, pos, os, parameters, e);
197 }