/** * Packer version 3.0 (final) * Copyright 2004-2007, Dean Edwards * Web: {@link http://dean.edwards.name/} * * This software is licensed under the MIT license * Web: {@link http://www.opensource.org/licenses/mit-license} * * Ported to Java by Pablo Santiago based on C# version by Jesse Hansen, <twindagger2k @ msn.com> * Web: {@link http://jpacker.googlecode.com/} * Email: <pablo.santiago @ gmail.com> */ package com.jpacker; import java.util.ArrayList; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; import com.jpacker.evaluators.DeleteEvaluator; import com.jpacker.evaluators.Evaluator; import com.jpacker.evaluators.IntegerEvaluator; import com.jpacker.evaluators.StringEvaluator; import com.jpacker.strategies.DefaultReplacementStrategy; import com.jpacker.strategies.ReplacementStrategy; /** * Parser class that matches RegGrp.js. * * This class parses the script using the expressions added via * {@link #remove(String)}, {@link #ignore(String)}, * {@link #replace(String,String)} and {@link #replace(String,Evaluator)} * * @author Pablo Santiago <pablo.santiago @ gmail.com> */ public class JPackerParser { private static Pattern GROUPS = Pattern.compile("\\("); private static Pattern SUB_REPLACE = Pattern.compile("\\$(\\d+)"); private static Pattern INDEXED = Pattern.compile("^\\$\\d+$"); private static Pattern ESCAPE = Pattern.compile("\\\\."); private static Pattern ESCAPE_BRACKETS = Pattern.compile("\\(\\?[:=!]|\\[[^\\]]+\\]"); private static Pattern DELETED = Pattern.compile("\\x01[^\\x01]*\\x01"); private static String IGNORE = "$0"; private List<JPackerPattern> jpatterns = new ArrayList<JPackerPattern>(); /** * Add an expression to be removed * * @param expression * Regular expression {@link String} */ public void remove(String expression) { replace(expression, ""); } /** * Add an expression to be ignored * * @param expression * Regular expression {@link String} */ public void ignore(String expression) { replace(expression, IGNORE); } /** * Add an expression to be replaced with the replacement string * * @param expression * Regular expression {@link String} * @param replacement * Replacement {@link String}. Use $1, $2, etc. for groups */ public void replace(String expression, String replacement) { if (replacement.isEmpty()) { replace(expression, new DeleteEvaluator()); return; } Evaluator evaluator; // does the pattern deal with sub-expressions? and a simple lookup (e.g. $2) if (SUB_REPLACE.matcher(replacement).matches() && INDEXED.matcher(replacement).matches()) { evaluator = new IntegerEvaluator(Integer.parseInt(replacement.substring(1))); } else { evaluator = new StringEvaluator(replacement); } JPackerPattern jpattern = new JPackerPattern(expression, evaluator); // count the number of sub-expressions jpattern.setLength(countSubExpressions(expression)); jpatterns.add(jpattern); } /** * Add an expression to be replaced using an {@link Evaluator} object * * @param expression * Regular expression String * @param evaluator * The {@link Evaluator} object */ public void replace(String expression, Evaluator evaluator) { JPackerPattern jpattern = new JPackerPattern(expression, evaluator); // count the number of sub-expressions jpattern.setLength(countSubExpressions(expression)); jpatterns.add(jpattern); } // builds the patterns into a single regular expression private Pattern buildPatterns() { StringBuilder rtrn = new StringBuilder(); for (JPackerPattern jpattern : jpatterns) { rtrn.append(jpattern).append("|"); } rtrn.deleteCharAt(rtrn.length() - 1); return Pattern.compile(rtrn.toString()); } /** * Executes the parser in order to parse the script with the expressions * added via {@link #remove(String)}, {@link #ignore(String)}, * {@link #replace(String,String)} and {@link #replace(String,Evaluator)} * * @param input * The script to be parsed * @return The parsed script */ public String exec(String input) { return exec(input, new DefaultReplacementStrategy()); } /** * Executes the parser in order to parse the script with the expressions * added via {@link #remove(String)}, {@link #ignore(String)}, * {@link #replace(String,String)} and {@link #replace(String,Evaluator)}. * Using a {@link ReplacementStrategy} object, a custom replacement * algorithm can be used. * * @param input * The script to be parsed * @param strategy * The {@link ReplacementStrategy} object for custom replacement * @return The parsed script */ public String exec(String input, ReplacementStrategy strategy) { Matcher matcher = buildPatterns().matcher(input); StringBuffer sb = new StringBuffer(input.length()); while (matcher.find()) { String rep = strategy.replace(jpatterns, matcher); if (rep != null && !rep.isEmpty()) { rep = Matcher.quoteReplacement(rep); } matcher.appendReplacement(sb, rep); } matcher.appendTail(sb); return DELETED.matcher(sb).replaceAll(""); } // count the number of sub-expressions private int countSubExpressions(String expression) { int cont = 0; Matcher matcher = GROUPS.matcher(internalEscape(expression)); while (matcher.find()) { cont++; } // - add 1 because each group is itself a sub-expression return cont + 1; } private String internalEscape(String str) { return ESCAPE_BRACKETS.matcher(ESCAPE.matcher(str).replaceAll("")).replaceAll(""); } /** * The patterns added to this {@link JPackerParser} object as a {@link List} * of {@link JPackerPattern} * * @return The {@link List} of {@link JPackerPattern} objects */ public List<JPackerPattern> getJPatterns() { return jpatterns; } }