]> git.saurik.com Git - wxWidgets.git/blame - contrib/src/applet/prepifelse.cpp
wxEmptyString decorations.
[wxWidgets.git] / contrib / src / applet / prepifelse.cpp
CommitLineData
716cd410
KB
1/****************************************************************************
2*
3* wxWindows HTML Applet Package
4*
5* Copyright (C) 1991-2001 SciTech Software, Inc.
6* All rights reserved.
7*
8* ========================================================================
9*
10* The contents of this file are subject to the wxWindows License
11* Version 3.0 (the "License"); you may not use this file except in
12* compliance with the License. You may obtain a copy of the License at
13* http://www.wxwindows.org/licence3.txt
14*
15* Software distributed under the License is distributed on an
16* "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
17* implied. See the License for the specific language governing
18* rights and limitations under the License.
19*
20* ========================================================================
21*
22* Language: ANSI C++
23* Environment: Any
24*
25* Description: This file is the implementation of the Preprocessor object
26* for parsing the <!--#if directive
27*
28****************************************************************************/
29
716cd410
KB
30// Include private headers
31#include "wx/applet/prepifelse.h"
32#include "wx/applet/ifelsevar.h"
574c939e
KB
33#include "wx/applet/echovar.h"
34#include "wx/string.h"
716cd410 35
574c939e
KB
36// Force link macro
37#include "wx/html/forcelnk.h"
38// wxWindows
39#include "wx/msgdlg.h"
716cd410
KB
40
41/*----------------------------- Implementation ----------------------------*/
42
43/* {SECRET} */
44/****************************************************************************
45REMARKS:
46None of the Reverse Find functions in wxWindows appear to work in a way that
47can be used by our code. This includes the libstr rfind implementations which
48do not correctly pass the given return value.
49****************************************************************************/
50int ReverseFind(
51 const wxString &tstr,
19193a2c
KB
52 const wxString &str,
53 int start = -1)
716cd410
KB
54{
55 wxASSERT( str.GetStringData()->IsValid() );
56
57 // TODO could be made much quicker than that
58 int p = tstr.Len()-str.Len()-1;
19193a2c
KB
59 int p2 = start-str.Len();
60
61 // if the user supplied a valid start point, use it
62 if (start != -1 && p > p2) p = p2;
716cd410
KB
63 while ( p >= 0 ) {
64 if ( wxStrncmp(tstr.c_str() + p, str.c_str(), str.Len()) == 0 )
65 return p;
66 p--;
67 }
716cd410
KB
68 return -1;
69}
70
574c939e
KB
71/* {SECRET} */
72/****************************************************************************
73REMARKS:
74tells if a character is a letter.
75replace this when wxWindows gets regex library. (without strange licensing
76restrictions)
77****************************************************************************/
78bool IsLetter(
79 char c, bool acceptspace = false)
80{
81 if (acceptspace && (c == ' ')) return true;
82 if (c >= '0' && c <= '9') return true;
83 return ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' || c == '\"' || c == '\'' );
84}
85
86#define IsQuote(c) (c == '\'' || c == '\"')
87
88/* {SECRET} */
89/****************************************************************************
90REMARKS:
91tells if a character is a letter.
92replace this when wxWindows gets regex library. (without strange licensing
93restrictions)
94****************************************************************************/
95wxString GetEquals(
96 wxString var,
97 wxString value)
98{
99 if (!wxEchoVariable::Exists(var)) {
100 // TODO: when we implement the set variable, check for a set variable as well
101 #ifdef CHECKED
102 wxMessageBox(wxString("wxHTML #if\\else error: Variable ") + var + wxString(" not found."),"Error",wxICON_ERROR);
103 #endif
104 return wxString("0"); // false
105 }
106
107 wxString tmp = wxEchoVariable::GetValue(var);
108
109 if (IsQuote( value.GetChar(0) ))
110 value = value.Mid(1);
111 if (IsQuote(value.GetChar(value.Length()-1)))
112 value = value.Mid(0,value.Length()-1);
113
114 if (tmp.CmpNoCase(value) == 0) return wxString("1");
115 return wxString("0");
116}
117
19193a2c
KB
118/****************************************************************************
119PARAMETERS:
120str - text of #if statement
121
122RETURNS:
123true or false depending on how it evaluated
124
125REMARKS:
574c939e 126TODO: rewrite this whole thing using regular expressions when they are done.
19193a2c
KB
127
128SEE ALSO:
129wxIfElseVariable
130****************************************************************************/
574c939e
KB
131bool ParseIfStatementValue(
132 wxString &str)
133{
19193a2c
KB
134 // Find out if the tag has parenthesis
135 // recursive to parse the text within the parenthesis,
136 // replacing the text with 1 or 0, (hardcoded true or false)
137 int b;
138 while ((b = str.Find('(')) != -1) {
139 int e;
140 // Find the matching parenthesis
141 int nextbeg, nextend;
142 int parencount = 1, min = b+1;
143 do {
19193a2c
KB
144 nextbeg = str.find('(', min);
145 nextend = str.find(')', min);
146 if (nextbeg < nextend && nextbeg != wxString::npos) {
147 parencount++;
148 min = nextbeg+1;
149 }
150 else {
151 parencount--;
152 min = nextend+1;
153 }
154
155 if (nextend == wxString::npos) {
574c939e 156#ifdef CHECKED
19193a2c 157 wxMessageBox("wxHTML #if\\else error: Unmatched parenthesis in #if expression.","Error",wxICON_ERROR);
574c939e 158#endif
19193a2c
KB
159 return true;
160 }
161 // once parencount reaches 0 again we have found our matchin )
162 } while (parencount > 0);
163
164 e = nextend;
165
166 // Extract the expression from the parenthesis block and recurse
167 // to solve it.
168 wxString tag;
169 tag = str.Mid(b+1, e-b-1);
170 bool val = ParseIfStatementValue(tag);
171 // Add extra spaces just in case of NOT(VAL)
172 if (val) str = str.Mid(0, b) + " 1" + str.Mid(e+1);
173 else str = str.Mid(0, b) + " 0" + str.Mid(e+1);
19193a2c
KB
174 }
175
176 // Remove spaces from left and right
177 str.Trim(false);
178 str.Trim(true);
179
180 // Convert text method of operators "AND" and "OR" to c style
181 // this makes only one special case necessary for each later on
182 str.Replace(" AND ", "&&");
183 str.Replace(" OR ", "||");
574c939e
KB
184 str.Replace(" EQUALS ", "==");
185
186 // Check for equals statements
187 // == statements are special because they are evaluated as a single block
188 int equ;
189 equ = str.find("==");
190 while (equ != wxString::npos) {
191 int begin, end;
192 int begin2, end2; // ends of words
193 begin = equ-1;
194 end = equ+2;
195
196 // remove spaces, find extents
197 while (end < str.Length() && str.GetChar(end) == ' ')
198 end++;
199 while (begin >= 0 && str.GetChar(begin) == ' ')
200 begin--;
201 end2 = end;
202 begin2 = begin;
203 if (str.GetChar(end2) == '\'' || str.GetChar(end2) == '\"') {
204 end2++;
205 while (end2 < str.Length() && str.GetChar(end2) != '\'' && str.GetChar(end2) != '\"' )
206 end2++;
207 end2++;
208 }
209 else {
210 while (end2 < str.Length() && IsLetter(str.GetChar(end2)))
211 end2++;
212 }
213 while (begin >= 0 && IsLetter(str.GetChar(begin)))
214 begin--;
215
216 if (begin < 0) begin = 0;
217 else begin++;
218 if (end2 >= str.Length()) end2 = str.Length();
219
220 wxString tmpeq = GetEquals(str.Mid(begin, begin2-begin+1), str.Mid(end, end2-end));
221 str = str.Mid(0, begin) + wxString(" ") + tmpeq + wxString(" ") +
222 str.Mid(end2);
223 equ = str.find("==");
224
225 // Remove spaces from left and right
226 str.Trim(false);
227 str.Trim(true);
228 }
19193a2c
KB
229
230 // We use ReverseFind so that the whole left expression gets evaluated agains
231 // the right single item, creating a left -> right evaluation
232 // Search for || operators, recurse to solve (so we don't have to handle special cases here)
233 int and, or;
234 and = ReverseFind(str, "&&");
235 or = ReverseFind(str, "||");
236 if ( (and != -1) || (or != -1) ) {
237 wxString tag1, tag2;
238 // handle the rightmost first to force left->right evaluation
574c939e 239 if ( (and > or) ) {
19193a2c
KB
240 return (
241 ParseIfStatementValue(tag2 = str.Mid(and+2)) &&
242 ParseIfStatementValue(tag1 = str.Mid(0, and)) );
243 }
244 else {
245 return (
246 ParseIfStatementValue(tag2 = str.Mid(or+2)) ||
247 ParseIfStatementValue(tag1 = str.Mid(0, or)) );
248 }
19193a2c
KB
249 }
250
251 // By the time we get to this place in the function we are guarenteed to have a single
252 // variable operation, perhaps with a NOT or ! operator
253 bool notval = false;
254
255 // search for a NOT or ! operator
256 if (str.Mid(0, 1) == "!") {
257 str.Remove(0, 1);
258 str.Trim(false); // trim spaces from left
259 notval = true;
260 }
261 else if (str.Mid(0,4).CmpNoCase("NOT ") == 0) {
262 str.Remove(0, 4);
263 str.Trim(false); // trim any extra spaces from left
264 notval = true;
265 }
266
267 // now all we have left is the name of the class or a hardcoded 0 or 1
19193a2c 268 if (str == "") {
574c939e 269#ifdef CHECKED
19193a2c 270 wxMessageBox("wxHTML #if\\else error: Empty expression in #if\\#elif statement.","Error",wxICON_ERROR);
574c939e 271#endif
19193a2c
KB
272 return true;
273 }
274
275 // check for hardcoded 0 and 1 cases, (these are used by parenthesis catcher)
276 // this just decomplicates the recursion algorithm
277 if (str == "0") return notval;
278 if (str == "1") return !notval;
279
280 // Grab the value from the variable class identified by cname
574c939e 281 bool value = wxIfElseVariable::GetValue(str);
19193a2c
KB
282 if (notval) value = !value;
283 return value;
284
285}
716cd410
KB
286/****************************************************************************
287PARAMETERS:
288text - HTML to process for if/else blocks
289
290RETURNS:
291The string containing the processed HTML
292
293REMARKS:
19193a2c 294This function replaces #if, #else, #elif, and #endif directives with the text
716cd410
KB
295contained within the blocks, dependant on the value of the given boolean
296variable. The variable is created by making a sub class of wxIfElseVariable.
297Dynamic class construction is used at run time internally to create an instance
298of this class and access the value of the variable.
299
300SEE ALSO:
301wxIfElseVariable
302****************************************************************************/
303wxString wxIfElsePrep::Process(
304 const wxString& text) const
305{
306 int b;
307 char ft[] = "<!--#if ";
19193a2c
KB
308 char ftend[] = "<!--#endif-->";
309 char ftelse[] = "<!--#else-->";
310 char ftnot[] = "<!--#if not ";
505710ca 311 char ftnot2[] = "<!--#if !";
19193a2c
KB
312 char ftelif[] = "<!--#elif ";
313
314 // make a copy so we can replace text as we go without affecting the original
716cd410 315 wxString output = text;
19193a2c
KB
316
317 // Avoid duplication of our parsing code by turning any #elif blocks into appropriate
318 // else/if blocks
319 while ((b = ReverseFind(output.Lower(), ftelif)) != -1) {
320 int e;
321 // Replace beginning of block
322 e = output.find("-->", b + strlen(ftelif));
323
324 if (e == wxString::npos) {
574c939e 325#ifdef CHECKED
19193a2c 326 wxMessageBox("wxHTML #elif error: Premature end of file while parsing #elif.","Error",wxICON_ERROR);
574c939e 327#endif
19193a2c
KB
328 break;
329 }
330
331 // Convert to lower case so find is easy, grab everything after #elif tag
332 wxString remains = (output.Mid(e+strlen("-->"))).Lower();
333
334 // find matching else or endif
335 int nextif, nextendif;
336 int ifcount = 1, min = 0;
337 do {
338 nextif = remains.find(ft, min);
339 nextendif = remains.find(ftend, min);
340 if (nextif < nextendif && nextif != wxString::npos) {
341 ifcount++;
342 min = nextif+1;
343 }
344 else {
345 ifcount--;
346 min = nextendif+1;
347 }
348
349 if (nextendif == wxString::npos) {
574c939e 350#ifdef CHECKED
19193a2c 351 wxMessageBox("wxHTML #elif error: Premature end of file before finding #endif.","Error",wxICON_ERROR);
574c939e 352#endif
19193a2c
KB
353 break;
354 }
355 // once ifcount reaches 0 again we have found our matchin #endif
356 } while (ifcount > 0);
357
358 // If it couldn't be found die gracefully
359 if (nextendif == wxString::npos) {
574c939e
KB
360 // We already displayed a message, just break all the way out
361 break;
362 }
19193a2c
KB
363
364 int elifsize = e - (b + strlen(ftelif)) + strlen("-->");
365 // Create the #if/else block, removing the #elif code
366 output = output.Mid(0, b) +
367 wxString(wxString(ftelse)+wxString(ft)) +
368 output.Mid(b+strlen(ftelif), elifsize+nextendif) +
369 wxString(ftend) +
370 output.Mid(b+strlen(ftelif)+elifsize+nextendif);
19193a2c
KB
371 }
372
373 // Parse out the if else blocks themselves
716cd410 374 while ((b = ReverseFind(output.Lower(), ft)) != -1) {
505710ca 375 // Loop until every #if directive is found
716cd410
KB
376 // We search from the end of the string so that #if statements will properly recurse
377 // and we avoid the hassle of matching statements with the correct <!--#endif-->
505710ca
KB
378 bool notval = false;
379 int off = 0;
19193a2c 380 int end;
716cd410
KB
381 wxString usecode, code;
382 wxString cname;
383 wxString tag;
384 bool value;
385
386 code = wxString("");
387
388 // grab the tag and get the name of the variable
389 end = (output.Mid(b)).Find("-->");
390 if (end == -1) {
391#ifdef CHECKED
392 wxMessageBox("wxHTML #if error: Premature end of file while parsing #if.","Error",wxICON_ERROR);
393#endif
394 break;
395 }
396
397 end += 3;
19193a2c
KB
398 // remove the <!--#if and --> sections from the tag before passing it on to be parsed
399 tag = output.Mid(b+strlen(ft), end-strlen(ft)-3);
716cd410
KB
400 output.Remove(b, end);
401
19193a2c 402 value = ParseIfStatementValue(tag);
716cd410
KB
403
404 // Find the end of the tag (<!--#endif-->) and copy it all into the variable code
19193a2c 405 end = ((output.Mid(b)).Lower()).Find(ftend);
716cd410
KB
406 if (end == -1) {
407#ifdef CHECKED
408 wxMessageBox("wxHTML #if error: Premature end of file while searching for matching #endif.","Error",wxICON_ERROR);
409#endif
410 break;
411 }
412
413 code = output.Mid(b, end);
19193a2c 414 output.Remove(b, end+strlen(ftend)); // remove the entire #if block from original document
716cd410
KB
415
416 // Find out if there is an else statement
19193a2c 417 end = (code.Lower()).Find(ftelse);
716cd410
KB
418 if (end != -1) {
419 if (!value) {
420 // Use the else statement
19193a2c 421 usecode = code.Mid(end+strlen(ftelse));
716cd410
KB
422 }
423 else {
424 // Use statement before #else
425 usecode = code.Mid(0, end);
426 }
427 }
428 else if (value) {
429 // There is no #else statement
430 usecode = code;
431 }
432
433 if (usecode != wxString(""))
434 output = (output.Mid(0,b) + usecode + output.Mid(b));
435 }
436
437 return output;
438}
439
574c939e
KB
440FORCE_LINK(ifelsevar)
441