001: /*
002: * Copyright (c) 2002-2008 Gargoyle Software Inc. All rights reserved.
003: *
004: * Redistribution and use in source and binary forms, with or without
005: * modification, are permitted provided that the following conditions are met:
006: *
007: * 1. Redistributions of source code must retain the above copyright notice,
008: * this list of conditions and the following disclaimer.
009: * 2. Redistributions in binary form must reproduce the above copyright notice,
010: * this list of conditions and the following disclaimer in the documentation
011: * and/or other materials provided with the distribution.
012: * 3. The end-user documentation included with the redistribution, if any, must
013: * include the following acknowledgment:
014: *
015: * "This product includes software developed by Gargoyle Software Inc.
016: * (http://www.GargoyleSoftware.com/)."
017: *
018: * Alternately, this acknowledgment may appear in the software itself, if
019: * and wherever such third-party acknowledgments normally appear.
020: * 4. The name "Gargoyle Software" must not be used to endorse or promote
021: * products derived from this software without prior written permission.
022: * For written permission, please contact info@GargoyleSoftware.com.
023: * 5. Products derived from this software may not be called "HtmlUnit", nor may
024: * "HtmlUnit" appear in their name, without prior written permission of
025: * Gargoyle Software Inc.
026: *
027: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
028: * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
029: * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GARGOYLE
030: * SOFTWARE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
031: * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
032: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
033: * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
034: * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
035: * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
036: * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
037: */
038: package com.gargoylesoftware.htmlunit.javascript;
039:
040: import java.util.HashSet;
041: import java.util.Set;
042: import java.util.regex.Matcher;
043: import java.util.regex.Pattern;
044:
045: import org.apache.commons.lang.ArrayUtils;
046:
047: import com.gargoylesoftware.htmlunit.BrowserVersion;
048: import com.gargoylesoftware.htmlunit.ScriptPreProcessor;
049: import com.gargoylesoftware.htmlunit.html.HtmlElement;
050: import com.gargoylesoftware.htmlunit.html.HtmlPage;
051:
052: /**
053: * A basic implementation for IE Conditional Compilation.
054: *
055: * <p>Currently provides (basic) supports for <code>@cc_on</code>, <code>@if</code>,
056: * <code>@el_if</code>, <code>@else</code>, <code>@end</code> and <b>@set</b>,
057: * as well as for conditional compilation variables:
058: * "@_win16", "@_mac", "@_alpha", "@_mc680x0", "@_PowerPC", "@_debug", "@_fast",
059: * "@_win32", "@_x86", "@_jscript", "@_jscript_version" and "@_jscript_build"
060: *
061: * @version $Revision: 2132 $
062: * @author Ahmed Ashour
063: * @author Marc Guillemot
064: *
065: * @see <a href="http://msdn2.microsoft.com/en-us/library/ahx1z4fs.aspx">Microsoft Docs</a>
066: */
067: public class IEConditionalCompilationScriptPreProcessor implements
068: ScriptPreProcessor {
069:
070: private static final String CC_VARIABLE_PREFIX = "htmlunit_cc_variable_";
071: private final Set setVariables_ = new HashSet();
072:
073: /**
074: * {@inheritDoc}
075: */
076: public String preProcess(final HtmlPage htmlPage,
077: final String sourceCode, final String sourceName,
078: final HtmlElement htmlElement) {
079:
080: final Pattern p = Pattern.compile("/\\*@cc_on(.*)@\\*/",
081: Pattern.DOTALL);
082: final Matcher m = p.matcher(sourceCode);
083: final StringBuffer sb = new StringBuffer();
084: final BrowserVersion browserVersion = htmlPage.getWebClient()
085: .getBrowserVersion();
086: while (m.find()) {
087: m.appendReplacement(sb, processConditionalCompilation(m
088: .group(1), browserVersion));
089: }
090: m.appendTail(sb);
091: return sb.toString();
092: }
093:
094: private String processConditionalCompilation(
095: final String precompilationBody,
096: final BrowserVersion browserVersion) {
097: String body = processIfs(precompilationBody);
098: body = replaceCompilationVariables(body, browserVersion);
099: body = processSet(body);
100: body = replaceCustomCompilationVariables(body);
101: return body;
102: }
103:
104: private String replaceCustomCompilationVariables(final String body) {
105: final Pattern p = Pattern.compile("@\\w+|'[^']*'|\"[^\"]*\"");
106: final Matcher m = p.matcher(body);
107: final StringBuffer sb = new StringBuffer();
108: while (m.find()) {
109: final String match = m.group();
110: if (match.startsWith("@")) {
111: m.appendReplacement(sb,
112: replaceOneCustomCompilationVariable(match));
113: } else {
114: m.appendReplacement(sb, match);
115: }
116: }
117: m.appendTail(sb);
118: return sb.toString();
119: }
120:
121: private String replaceOneCustomCompilationVariable(
122: final String variable) {
123: if (setVariables_.contains(variable)) {
124: return CC_VARIABLE_PREFIX + variable.substring(1);
125: }
126: return "NaN";
127: }
128:
129: private String processSet(final String body) {
130: final Pattern p = Pattern
131: .compile("@set\\s+(@\\w+)(\\s*=\\s*[\\d\\.]+)");
132: final Matcher m = p.matcher(body);
133: final StringBuffer sb = new StringBuffer();
134: while (m.find()) {
135: setVariables_.add(m.group(1));
136: m.appendReplacement(sb, CC_VARIABLE_PREFIX
137: + m.group(1).substring(1) + m.group(2) + ";");
138: }
139: m.appendTail(sb);
140: return sb.toString();
141: }
142:
143: private String processIfs(String code) {
144: code = code.replaceAll("@if\\s*\\(([^\\)]+)\\)", "if ($1) {");
145: code = code.replaceAll("@elif\\s*\\([^\\)]+\\)",
146: "} else if ($1) {");
147: code = code.replaceAll("@else", "} else {");
148: code = code.replaceAll("@end", "}");
149: return code;
150: }
151:
152: String replaceCompilationVariables(final String source,
153: final BrowserVersion browserVersion) {
154: final Pattern p = Pattern
155: .compile("(@_\\w+)|'[^']*'|\"[^\"]*\"");
156: final Matcher m = p.matcher(source);
157: final StringBuffer sb = new StringBuffer();
158: while (m.find()) {
159: final String match = m.group();
160: if (match.startsWith("@")) {
161: m.appendReplacement(sb, replaceOneVariable(match,
162: browserVersion));
163: } else {
164: m.appendReplacement(sb, match);
165: }
166: }
167: m.appendTail(sb);
168: return sb.toString();
169: }
170:
171: /**
172: * Replace a single conditional compilation variable
173: * @param variable something like "@_win32"
174: * @return the value
175: */
176: private String replaceOneVariable(final String variable,
177: final BrowserVersion browserVersion) {
178: final String[] varNaN = { "@_win16", "@_mac", "@_alpha",
179: "@_mc680x0", "@_PowerPC", "@_debug", "@_fast" };
180: final String[] varTrue = { "@_win32", "@_x86", "@_jscript" };
181:
182: if (ArrayUtils.contains(varTrue, variable)) {
183: return "true";
184: } else if ("@_jscript_version".equals(variable)) {
185: if (browserVersion.getBrowserVersionNumeric() <= 6) {
186: return "5.6";
187: } else {
188: return "5.7";
189: }
190: } else if ("@_jscript_build".equals(variable)) {
191: if (browserVersion.getBrowserVersionNumeric() <= 6) {
192: return "6626"; // that's what my IE6 currently returns
193: } else {
194: return "5730";
195: }
196: } else if (ArrayUtils.contains(varNaN, variable)) {
197: return "NaN";
198: }
199: return variable;
200: }
201: }
|