/** * Sahi - Web Automation and Test Tool * * Copyright 2006 V Narayan Raman * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package net.sf.sahi.playback; import java.io.ByteArrayOutputStream; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.Iterator; import java.util.Properties; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; import net.sf.sahi.config.Configuration; import net.sf.sahi.report.LogViewer; import net.sf.sahi.util.Utils; import org.apache.log4j.Logger; public abstract class SahiScript { private static Logger logger = Logger.getLogger(SahiScript.class); private static ArrayList<String> actionKeywords; private static ArrayList<String> normalKeywords; private static Pattern pattern_set; private static Pattern pattern_popup_set; protected String script; private static final String PREFIX = "_sahi.schedule(\""; private static final String CONJUNCTION = "\", \""; private static final String SUFFIX = "\");"; private String filePath; protected String scriptName; protected ArrayList<String> parents; private String original = ""; private static final Pattern REG_EXP_FOR_ADDING = Pattern.compile(getRegExp(false)); private static final Pattern REG_EXP_FOR_STRIPPING = Pattern.compile(getRegExp(true)); private static final Pattern REG_EXP_FOR_ACTIONS = Pattern.compile(getActionRegExp()); protected String path; private String jsString; private String browserJS; private String browserJSWithLineNumbers; private ArrayList<String> lineDebugInfo = new ArrayList<String>(); static { pattern_set = Pattern.compile("_set\\s*\\(.*"); pattern_popup_set = Pattern.compile("_popup\\s*\\(.*\\)\\s*[.]\\s*_set\\s*\\(.*"); } public SahiScript(String filePath, ArrayList<String> parents, String scriptName) { this.filePath = filePath; this.scriptName = scriptName; this.parents = parents; init(filePath); } protected void setScript(final String s) { original = s; browserJS = modifyFunctionNames(extractBrowserJS(s, false)); browserJSWithLineNumbers = modifyFunctionNames(extractBrowserJS(s, true)); jsString = modify(removeBrowserJS(s)); script = jsString; } String removeBrowserJS(String s) { StringBuffer sb = new StringBuffer(s); int startIx = s.indexOf("<browser>"); while (startIx != -1) { int endIx = s.indexOf("</browser>", startIx); if (endIx == -1) endIx = s.length(); else endIx = endIx + 10; String browserJSSnippet = s.substring(startIx, endIx); browserJSSnippet = browserJSSnippet.replaceAll("[^\\n]", " "); sb.replace(startIx, endIx, browserJSSnippet); startIx = s.indexOf("<browser>", endIx); } return sb.toString(); // return s.replaceAll("(?s)<browser>.*?</browser>", ""); } String extractBrowserJS(String s, boolean addLineNumberInfo) { StringBuffer sb = new StringBuffer(s); int startIx = 0;//s.indexOf("<browser>"); while (startIx != -1) { int endIx = s.indexOf("<browser>", startIx); if (endIx == -1) endIx = s.length(); else endIx = endIx + 9; String nonBrowserJSSnippet = s.substring(startIx, endIx); nonBrowserJSSnippet = nonBrowserJSSnippet.replaceAll("[^\\n]", " "); sb.replace(startIx, endIx, nonBrowserJSSnippet); startIx = s.indexOf("</browser>", endIx); } String js = sb.toString(); if (!addLineNumberInfo) { return stripBlankLines(js); } else return LogViewer.addLineNumbers(js, filePath); } private String stripBlankLines(String js) { String[] lines = js.split("\n"); StringBuffer sb2 = new StringBuffer(); int len = lines.length; for (int i = 0; i < len; i++) { String line = lines[i]; if ("".equals(line.trim())) continue; sb2.append(line + "\n"); } return sb2.toString(); } String extractBrowserJS2(String s, boolean addLineNumberInfo) { StringBuffer sb = new StringBuffer(); int startIx = s.indexOf("<browser>"); while (startIx != -1) { int endIx = s.indexOf("</browser>", startIx); if (endIx == -1) endIx = s.length(); sb.append(s.substring(startIx + 9, endIx)); sb.append("\n"); startIx = s.indexOf("<browser>", endIx + 10); } String withoutLineNumbers = sb.toString(); return withoutLineNumbers; } public String jsString() { return jsString; } static String addJSEvalCode(String str) { StringBuffer sb = new StringBuffer(); sb.append("_sahi.execSteps = \""); sb.append(Utils.makeString(str)); sb.append("\";\neval(_sahi.execSteps);"); return sb.toString(); } public String modifiedScript() { return script == null ? "" : script; } String modify(String s) { StringBuffer sb = new StringBuffer(); s = normalizeNewLinesForOSes(s); Iterator<?> tokens = Utils.getTokens(s).iterator(); int lineNumber = 0; while (tokens.hasNext()) { lineNumber++; String line = ((String) tokens.next()); if ("".equals(line)) { continue; } boolean isInclude = false; line = line.trim(); if (line.startsWith("_include")) { SahiScript included = processInclude(line); if (included != null) { sb.append(included.jsString); browserJS += included.browserJS; browserJSWithLineNumbers += included.browserJSWithLineNumbers; lineDebugInfo.addAll(included.lineDebugInfo); isInclude = true; } } else if (line.contains("_condition")) { sb.append(modifyCondition(line, lineNumber)); } else if (line.startsWith("_wait")) { sb.append(modifyWait(line, lineNumber)); } else if (isSet(line)) { sb.append(processSet(line, lineNumber)); } else if (line.startsWith("_") && lineStartsWithActionKeyword(line)) { sb.append(scheduleLine(line, lineNumber)); } else { sb.append(modifyLine(line)); } if (!isInclude) lineDebugInfo.add(getDebugInfo(lineNumber)); } String toString = sb.toString(); logger.debug("modified script:" + toString); return toString; } String normalizeNewLinesForOSes(String s) { return s.replace("\r\n", "\n").replace("\r", "\n"); } boolean isSet(String line) { return pattern_set.matcher(line).matches() || pattern_popup_set.matcher(line).matches(); } String processSet(String line, int lineNumber) { String patternStr = "(.*)_set\\s*\\(\\s*([^,]*),\\s*(.*)\\)"; Pattern pattern = Pattern.compile(patternStr); Matcher matcher = pattern.matcher(line); boolean matchFound = matcher.find(); if (!matchFound) { return modifyLine(line); } else { String popupPrefix = matcher.group(1); String varName = matcher.group(2); String varValue = matcher.group(3); StringBuffer sb = new StringBuffer(); String tempVarName = varName.replaceAll("[$]", "\\\\\\$"); logger.debug("tempVarName="+tempVarName); sb.append(scheduleLine(popupPrefix + "_sahi.setServerVar('" + tempVarName + "', " + varValue + ");", lineNumber, true)); sb.append(modifyLine(varName + " = _sahi.getServerVar('" + tempVarName + "');")); return sb.toString(); } } String modifyWait(String line, int lineNumber) { int comma = line.indexOf(","); if (comma == -1) { return convertToExecuteWait(scheduleLine(line, lineNumber)); } StringBuffer sb = new StringBuffer(); sb.append(line.substring(0, comma)); sb.append(", "); int close = line.lastIndexOf(")"); if (close == -1) { close = line.length(); } sb.append(line.substring(comma + 1, close).trim()); sb.append(");"); return convertToExecuteWait(scheduleLine(sb.toString(), lineNumber)); } private String convertToExecuteWait(String s) { return s.replaceFirst("_sahi[.]schedule", "_sahi.executeWait"); } private String modifyLine(String line) { StringBuffer sb = new StringBuffer(); sb.append(modifyFunctionNames(line)); sb.append("\r\n"); return sb.toString(); } private String scheduleLine(String line, int lineNumber) { return scheduleLine(line, lineNumber, true); } private String scheduleLine(String line, int lineNumber, boolean separate) { StringBuffer sb = new StringBuffer(); sb.append(PREFIX); if (separate) { line = separateVariables(line); } sb.append(modifyFunctionNames(line)); sb.append(CONJUNCTION); sb.append(getDebugInfo(lineNumber)); sb.append(SUFFIX); sb.append("\r\n"); return sb.toString(); } public String getDebugInfo(int lineNumber) { StringBuffer sb = new StringBuffer(); sb.append(Utils.escapeDoubleQuotesAndBackSlashes(getDebugFilePath())); sb.append("&n="); sb.append(lineNumber); return sb.toString(); } protected String getDebugFilePath() { return filePath; } public String getLineDebugInfo(int lineNumber) { return (lineNumber != -1 && lineNumber < lineDebugInfo.size()) ? lineDebugInfo.get(lineNumber) : ""; } public static boolean lineStartsWithActionKeyword(String line) { return REG_EXP_FOR_ACTIONS.matcher(line).matches(); } public static String modifySingleLine(String line) { if (line.startsWith("_") && lineStartsWithActionKeyword(line)) { StringBuilder sb = new StringBuilder(); sb.append(PREFIX); sb.append(modifyFunctionNames(separateVariables(line))); sb.append(SUFFIX); return sb.toString(); } else { return modifyFunctionNames(line); } } @SuppressWarnings("unchecked") private SahiScript processInclude(String line) { final String include = getInclude(line); if (include != null && !isRecursed(include)) { ArrayList<String> clone = (ArrayList<String>) parents.clone(); clone.add(this.path); return new ScriptFactory().getScript(getFQN(include), clone); } return null; } private boolean isRecursed(final String include) { return parents.contains(getFQN(include)); } abstract String getFQN(String scriptName); static String getInclude(String line) { final String re = ".*_include[\\s]*\\([\"|'](.*)[\"|']\\).*"; Pattern p = Pattern.compile(re); Matcher m = p.matcher(line.trim()); if (m.matches()) { return line.substring(m.start(1), m.end(1)); } return null; } static String getActionRegExp() { ArrayList<String> keywords = getActionKeyWords(); return getActionRegExp(keywords); } static String getActionRegExp(ArrayList<String> keywords) { StringBuffer sb = new StringBuffer(); int size = keywords.size(); sb.append("^(?:"); for (int i = 0; i < size; i++) { String keyword = (String) keywords.get(i); sb.append(keyword); if (i != size - 1) { sb.append("|"); } } sb.append(")\\s*\\(.*"); return sb.toString(); } public static String modifyFunctionNames(String unmodified) { unmodified = stripSahiFromFunctionNames(unmodified); return REG_EXP_FOR_ADDING.matcher(unmodified).replaceAll("_sahi.$1$2"); } public static String stripSahiFromFunctionNames(String unmodified) { if (unmodified == null) return ""; return REG_EXP_FOR_STRIPPING.matcher(unmodified).replaceAll("$1$2"); } static String getRegExp(boolean isForStripping) { ArrayList<String> keywords = getKeyWords(); return getRegExp(isForStripping, keywords); } static String getRegExp(boolean isForStripping, ArrayList<String> keywords) { StringBuffer sb = new StringBuffer(); int size = keywords.size(); if (isForStripping) { sb.append("_sahi."); } sb.append("_?("); for (int i = 0; i < size; i++) { String keyword = (String) keywords.get(i); sb.append(keyword); if (i != size - 1) { sb.append("|"); } } sb.append(")(\\s*\\()"); return sb.toString(); } public static ArrayList<String> getActionKeyWords() { if (actionKeywords == null) { actionKeywords = loadActionKeyWords(); } return actionKeywords; } static ArrayList<String> loadActionKeyWords() { ArrayList<String> keywords = new ArrayList<String>(); keywords.addAll(loadKeyWords("scheduler")); return keywords; } public static ArrayList<String> getKeyWords() { if (normalKeywords == null) { normalKeywords = loadKeyWords(); } return normalKeywords; } static ArrayList<String> loadKeyWords() { ArrayList<String> keywords = new ArrayList<String>(); keywords.addAll(loadKeyWords("scheduler")); keywords.addAll(loadKeyWords("normal")); return keywords; } @SuppressWarnings("unchecked") static ArrayList<String> loadKeyWords(String type) { ArrayList<String> keywords = new ArrayList(); Properties fns = new Properties(); try { String typePath = Utils.concatPaths(Configuration.getConfigPath(), type + "_functions.txt"); fns.load(new FileInputStream(typePath)); } catch (Exception e) { } keywords.addAll((Set<String>) (Set) fns.keySet()); return keywords; } static String separateVariables(String s) { StringBuffer sb = new StringBuffer(); char c = ' '; boolean isVar = false; boolean escaped = false; boolean doubleQuoted = false; boolean quoted = false; int len = s.length(); int bracket = 0; int square = 0; for (int i = 0; i < len; i++) { c = s.charAt(i); if (c == '\\') { escaped = !escaped; } else if (c == '"') { if (!(escaped || quoted)) { doubleQuoted = !doubleQuoted; } } else if (c == '\'') { if (!(escaped || doubleQuoted)) { quoted = !quoted; } } else if (c == '$' && !isVar && !escaped && !quoted && !doubleQuoted && i + 1 < len && Character.isJavaIdentifierStart(s.charAt(i + 1))) { isVar = true; bracket = 0; square = 0; sb.append("\"+s_v("); } else if (isVar && !escaped && !(Character.isJavaIdentifierPart(c) || c == '.')) { boolean append = false; if (!escaped && !quoted && !doubleQuoted) { if (c == '(') { bracket++; } else if (c == ')') { bracket--; if (bracket < 0) { append = true; } } else if (c == '[') { square++; } else if (c == ']') { square--; if (square < 0) { append = true; } } else { if (bracket > 0 && (c == ',' || Character.isWhitespace(c) || c == '/')) { append = false; } else { append = true; } } } if (append) { sb.append(")+\""); isVar = false; } } if (!isVar && (c == '\\' || c == '"')) { sb.append('\\'); } sb.append(c); if (c != '\\') { escaped = false; } } if (isVar) { // $var was at the end. like $a==$b sb.append(")+\""); isVar = false; } return sb.toString(); } String findCondition(String s) { char c = ' '; boolean escaped = false; boolean doubleQuoted = false; boolean quoted = false; int len = s.length(); int bracket = 0; int i = 0; i = s.indexOf("_condition"); i = s.indexOf("(", i) + 1; if (i == 0) { return null; } int start = i; int end = -1; for (; i < len; i++) { c = s.charAt(i); if (c == '\\') { escaped = !escaped; } if (!escaped && !(Character.isJavaIdentifierPart(c) || c == '.')) { if (c == '"') { if (!(escaped || quoted)) { doubleQuoted = !doubleQuoted; } } else if (c == '\'') { if (!(escaped || doubleQuoted)) { quoted = !quoted; } } else if (!escaped && !quoted && !doubleQuoted) { if (c == '(') { bracket++; } else if (c == ')') { bracket--; if (bracket < 0) { end = i; break; } } } } if (c != '\\') { escaped = false; } } if (end == -1) { return null; } return s.substring(start, end); } // public String modifyWhile(String s, int lineNumber) { // return modifyIfWhile(s, lineNumber, "while (true) {\r\n", // "if (\"true\" != _sahi.getServerVar(\"condn\")) {break;} "); // } // // public String modifyIf(String s, int lineNumber) { // return modifyIfWhile(s, lineNumber, "", // "if (\"true\" == _sahi.getServerVar(\"condn\")"); // } public String modifyCondition(String s, int lineNumber) { if (s.indexOf("_condition") == -1) { return modifyLine(s); } String condition = findCondition(s); if (condition == null) { return modifyLine(s); } StringBuffer sb = new StringBuffer(); String prefix = s.substring(0, s.indexOf(condition)); String suffix = s.substring(s.indexOf(condition) + condition.length()); sb.append(prefix); String separated = "\"" + separateVariables(condition) + "\""; sb.append(separated); logger.debug(separated); sb.append(", \"" + getDebugInfo(lineNumber) + "\""); sb.append(suffix); return modifyFunctionNames(sb.toString()); } // public String modifyIfWhile(String s, int lineNumber, String prefix, // String suffix) { // if (s.indexOf("_condition") == -1) { // return modifyLine(s); // } // String condition = findCondition(s); // if (condition == null) { // return modifyLine(s); // } // StringBuffer sb = new StringBuffer(); // sb.append(prefix); // sb.append(scheduleLine("_sahi.saveCondition(" + condition + ");", // lineNumber)); // sb.append(suffix); // int end = s.indexOf(condition) + condition.length() + 1; // sb.append(s.substring(end)); // return sb.toString(); // } protected String read(final InputStream in) { ByteArrayOutputStream out = new ByteArrayOutputStream(); int c = ' '; try { while ((c = in.read()) != -1) { out.write(c); } } catch (IOException ste) { ste.printStackTrace(); } return new String(out.toByteArray()); } public String getScriptName() { return scriptName; } protected void init(final String url) { this.path = url; loadScript(url); } protected abstract void loadScript(String url); public String getOriginal() { return original; } public String getOriginalInHTML() { return SahiScriptHTMLAdapter.createHTML(original); } public String getFilePath() { return filePath; } public String getBrowserJS() { return browserJS; } /* * Called from lib.js */ public void addIncludeInfo(SahiScript included) { browserJS += included.browserJS; browserJSWithLineNumbers += included.browserJSWithLineNumbers; lineDebugInfo.addAll(included.lineDebugInfo); } public String getBrowserJSWithLineNumbers() { return browserJSWithLineNumbers; } }