2 * Copyright (C) 2008 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/)
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public License
15 * along with this library; see the file COPYING.LIB. If not, write to
16 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
24 #include "WMLVariables.h"
26 #include "WMLDocument.h"
27 #include <wtf/ASCIICType.h>
31 // WML variables specification, excluding the
32 // pre-WML 1.0 deprecated variable syntax
34 // varname = ("_" | alpha) ("_" | alpha | digit)*
35 // conv = ":" ("e" ("scape")? | "n" ("oesc")? | "u" ("nesc")?)
36 // var = ("$" varname) | ("$(" varname (conv)? ")")
38 static bool isValidFirstVariableNameCharacter(const UChar& character)
40 return WTF::isASCIIAlpha(character)
44 static bool isValidVariableNameCharacter(const UChar& character)
46 return WTF::isASCIIAlpha(character)
47 || WTF::isASCIIDigit(character)
51 static bool isValidVariableEscapingModeString(const String& mode, WMLVariableEscapingMode& escapeMode)
53 if (mode == "e" || mode == "escape")
54 escapeMode = WMLVariableEscapingEscape;
55 else if (mode == "u" || mode == "unesc")
56 escapeMode = WMLVariableEscapingUnescape;
57 else if (mode == "n" || mode == "noesc")
58 escapeMode = WMLVariableEscapingNone;
65 bool isValidVariableName(const String& name)
70 const UChar* characters = name.characters();
71 if (!isValidFirstVariableNameCharacter(characters[0]))
74 unsigned length = name.length();
75 for (unsigned i = 1; i < length; ++i) {
76 if (!isValidVariableNameCharacter(characters[i]))
83 bool containsVariableReference(const String& text, bool& isValid)
86 bool foundReference = false;
87 bool finished = false;
88 int currentPosition = 0;
89 const UChar* characters = text.characters();
92 // Find beginning of variable reference
93 int referenceStartPosition = text.find('$', currentPosition);
94 if (referenceStartPosition == -1) {
99 foundReference = true;
101 int nameStartPosition = referenceStartPosition + 1;
102 int nameEndPosition = -1;
104 if (characters[nameStartPosition] == '(') {
105 // If the input string contains an open brace, a close brace must exist as well
106 nameEndPosition = text.find(')', nameStartPosition + 1);
107 if (nameEndPosition == -1) {
115 int length = text.length();
116 for (nameEndPosition = nameStartPosition; nameEndPosition < length; ++nameEndPosition) {
117 if (!isValidVariableNameCharacter(text[nameEndPosition]))
122 if (nameEndPosition < nameStartPosition) {
128 // Eventually split of conversion string, and check its syntax afterwards
129 String conversionString;
130 String variableName = text.substring(nameStartPosition, nameEndPosition - nameStartPosition);
132 int conversionStringStart = variableName.find(':');
133 if (conversionStringStart != -1) {
134 conversionString = variableName.substring(conversionStringStart + 1, variableName.length() - (conversionStringStart + 1));
135 variableName = variableName.left(conversionStringStart);
138 isValid = isValidVariableName(variableName);
144 if (!conversionString.isEmpty()) {
145 isValid = isValidVariableName(conversionString);
151 WMLVariableEscapingMode escapeMode = WMLVariableEscapingNone;
152 isValid = isValidVariableEscapingModeString(conversionString, escapeMode);
159 currentPosition = nameEndPosition;
162 return foundReference;
165 String substituteVariableReferences(const String& reference, Document* document, WMLVariableEscapingMode escapeMode)
169 if (reference.isEmpty())
172 WMLPageState* pageState = wmlPageStateForDocument(document);
177 String remainingInput = reference;
180 while (!remainingInput.isEmpty()) {
183 int start = remainingInput.find("$");
185 // Consume all remaining characters, as there's nothing more to substitute
186 result += remainingInput;
190 // Consume all characters until the variable reference beginning
191 result += remainingInput.left(start);
192 remainingInput.remove(0, start);
194 // Transform adjacent dollar signs into a single dollar sign as string literal
195 if (remainingInput[1] == '$') {
197 remainingInput.remove(0, 2);
202 String conversionMode;
204 if (remainingInput[1] == '(') {
205 int referenceEndPosition = remainingInput.find(")");
206 if (referenceEndPosition == -1) {
211 variableName = remainingInput.substring(2, referenceEndPosition - 2);
212 remainingInput.remove(0, referenceEndPosition + 1);
214 // Determine variable conversion mode string
215 int pos = variableName.find(':');
217 conversionMode = variableName.substring(pos + 1, variableName.length() - (pos + 1));
218 variableName = variableName.left(pos);
221 int length = remainingInput.length();
222 int referenceEndPosition = 1;
224 for (; referenceEndPosition < length; ++referenceEndPosition) {
225 if (!isValidVariableNameCharacter(remainingInput[referenceEndPosition]))
229 variableName = remainingInput.substring(1, referenceEndPosition - 1);
230 remainingInput.remove(0, referenceEndPosition);
233 isValid = isValidVariableName(variableName);
237 ASSERT(!variableName.isEmpty());
239 String variableValue = pageState->getVariable(variableName);
240 if (variableValue.isEmpty())
243 if (containsVariableReference(variableValue, isValid)) {
247 variableValue = substituteVariableReferences(variableValue, document, escapeMode);
251 if (!conversionMode.isEmpty()) {
252 // Override default escape mode, if desired
253 WMLVariableEscapingMode specifiedEscapeMode = WMLVariableEscapingNone;
254 if ((isValid = isValidVariableEscapingModeString(conversionMode, specifiedEscapeMode)))
255 escapeMode = specifiedEscapeMode;
261 switch (escapeMode) {
262 case WMLVariableEscapingNone:
264 case WMLVariableEscapingEscape:
265 variableValue = encodeWithURLEscapeSequences(variableValue);
267 case WMLVariableEscapingUnescape:
268 variableValue = decodeURLEscapeSequences(variableValue);
272 result += variableValue;
277 reportWMLError(document, WMLErrorInvalidVariableReference);