trunk contents moved to root

This commit is contained in:
Jindra Petřík
2014-05-10 20:50:57 +02:00
parent 1b851e66a8
commit 199a4d0c2b
2296 changed files with 0 additions and 0 deletions

View File

@@ -0,0 +1,197 @@
/**
* 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 com.jpacker.exceptions.EmptyFileException;
import jargs.gnu.CmdLineParser;
import jargs.gnu.CmdLineParser.IllegalOptionValueException;
import jargs.gnu.CmdLineParser.Option;
import jargs.gnu.CmdLineParser.UnknownOptionException;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
/**
*
* @author Pablo Santiago <pablo.santiago @ gmail.com>
*/
public class JPacker {
/**
* @param args
* the command line arguments
*/
public static void main(String[] args) throws FileNotFoundException, IOException {
long startTime = System.currentTimeMillis();
CmdLineParser parser = new CmdLineParser();
Option baseOpt = parser.addIntegerOption('b', "base");
Option columnsOpt = parser.addIntegerOption('c', "column");
Option helpOpt = parser.addBooleanOption('h', "help");
Option minifyOpt = parser.addBooleanOption('m', "minify");
Option outputFilenameOpt = parser.addStringOption('o', "output");
Option quietOpt = parser.addBooleanOption('q', "quiet");
Option shrinkVariablesOpt = parser.addBooleanOption('s', "shrink-variables");
Writer out = null;
BufferedReader in = null;
try {
if (args.length == 0) {
throw new RuntimeException("No input file");
}
parser.parse(args);
Boolean help = (Boolean) parser.getOptionValue(helpOpt);
if (help != null && help.booleanValue()) {
printUsage();
System.exit(0);
}
boolean minify = parser.getOptionValue(minifyOpt) != null;
boolean shrinkVariables = parser.getOptionValue(shrinkVariablesOpt) != null;
boolean quiet = parser.getOptionValue(quietOpt) != null;
Integer base = (Integer) parser.getOptionValue(baseOpt);
Integer columns = (Integer) parser.getOptionValue(columnsOpt);
if (parser.getRemainingArgs().length == 0) {
throw new FileNotFoundException("No input file was provided");
}
String inputFilename = parser.getRemainingArgs()[0];
String outputFilename = (String) parser.getOptionValue(outputFilenameOpt);
JPackerExecuter executer;
if (base == null) {
executer = new JPackerExecuter(JPackerEncoding.NONE);
} else {
executer = new JPackerExecuter(getEncoding(baseOpt, base));
}
in = new BufferedReader(new FileReader(new File(inputFilename)));
String unpacked = buildStringFromTextFile(in);
if (unpacked.isEmpty()) {
throw new EmptyFileException("The file is empty");
}
String packed = executer.pack(unpacked, minify, shrinkVariables);
if (outputFilename == null) {
out = new OutputStreamWriter(System.out);
} else {
out = new OutputStreamWriter(new FileOutputStream(outputFilename));
}
if (columns != null) {
packed = wrapLines(packed, columns);
}
out.write(packed.replace("\n", System.getProperty("line.separator")));
out.close();
if (!quiet) {
long endTime = System.currentTimeMillis();
System.out.printf("Reduced to %.2f%% of its original size in %.4f seconds.\n",
((double) packed.length() / (double) unpacked.length()) * 100,
(double) (endTime - startTime) / 1000);
}
} catch (FileNotFoundException e) {
System.out.println("File not found: " + e.getLocalizedMessage());
System.exit(1);
} catch (IllegalOptionValueException e) {
System.out.println("Illegal option: " + e.getValue() + " - " + e.getOption());
System.exit(1);
} catch (UnknownOptionException e) {
printUsage();
System.exit(1);
} catch (Exception e) {
System.out.println(e.getLocalizedMessage());
System.exit(1);
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
System.out.println(e.getLocalizedMessage());
}
}
if (out != null) {
try {
out.close();
} catch (IOException e) {
System.out.println(e.getLocalizedMessage());
}
}
}
}
private static void printUsage() {
System.out.println("\nUsage: java -jar jpacker-x.y.z.jar [options] [input file]\n\n"
+ "Options\n"
+ " -b, --base Encoding base. Options are: 10, 36, 52, 62 \n"
+ " and 95. Ignored if --minify option is set.\n"
+ " -c, --column <column> Insert a line break after the specified column \n"
+ " number.\n"
+ " -h, --help Displays this information.\n"
+ " -m, --minify Minify only, do not obfuscate.\n"
+ " --minify and --base values will be ignored.\n"
+ " -o <file>, --output <file> Place the output into <file>.\n"
+ " Defaults to stdout.\n"
+ " -q, --quiet Quiet mode, no message.\n"
+ " -s, --shrink-variables Shrink variables. Ignored if --minify option \n"
+ " is set.\n");
}
private static String wrapLines(String packedScript, Integer columns) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < packedScript.length(); i++) {
int end = ((i + columns) > (packedScript.length()) ? packedScript.length() : i + columns);
sb.append(packedScript.substring(i, end)).append(System.getProperty("line.separator"));
i = end + 1;
}
return sb.toString();
}
private static String buildStringFromTextFile(BufferedReader reader) throws FileNotFoundException, IOException {
StringBuilder sb = new StringBuilder();
String s;
while ((s = reader.readLine()) != null) {
sb.append(s).append(System.getProperty("line.separator"));
}
reader.close();
return sb.toString();
}
private static JPackerEncoding getEncoding(Option option, Integer base) throws IllegalOptionValueException {
switch (base) {
case 10:
return JPackerEncoding.NUMERIC;
case 36:
return JPackerEncoding.MID;
case 52:
return JPackerEncoding.BASIC;
case 62:
return JPackerEncoding.NORMAL;
case 95:
return JPackerEncoding.HIGH_ASCII;
default:
throw new IllegalOptionValueException(option, "Encoding base option not valid");
}
}
}

View File

@@ -0,0 +1,75 @@
/**
* 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 com.jpacker.encoders.BasicEncoder;
import com.jpacker.encoders.Encoder;
import com.jpacker.encoders.HighAsciiEncoder;
import com.jpacker.encoders.MidEncoder;
import com.jpacker.encoders.NormalEncoder;
import com.jpacker.encoders.NumericEncoder;
/**
* Enum of encoding levels
*
* @author Pablo Santiago <pablo.santiago @ gmail.com>
*
*/
public enum JPackerEncoding {
/**
* No encoding
*/
NONE(0, "", null),
/**
* Base<sub>10</sub> : [0-9]
*/
NUMERIC(10, "String", new NumericEncoder()),
/**
* Base<sub>36</sub> : [0-z]
*/
MID(36, "function(c){return c.toString(a)}", new MidEncoder()),
/**
* Base<sub>52</sub> : [a-Z]
*/
BASIC(52, "function(c){return(c<a?'':e(parseInt(c/a)))+((c=c%a)>25?String.fromCharCode(c+39):String.fromCharCode(c+97));", new BasicEncoder()),
/**
* Base<sub>62</sub> : [0-Z]
*/
NORMAL(62, "function(c){return(c<a?'':e(parseInt(c/a)))+((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))}", new NormalEncoder()),
/**
* Base<sub>95</sub> : [¡-ÿ]
*/
HIGH_ASCII(95, "function(c){return(c<a?\"\":e(c/a))String.fromCharCode(c%a+161)}", new HighAsciiEncoder());
private final int encodingBase;
private final String encode;
private Encoder encoder;
JPackerEncoding(int encodingBase, String encode, Encoder encoder) {
this.encodingBase = encodingBase;
this.encode = encode;
this.encoder = encoder;
}
public int getEncodingBase() {
return encodingBase;
}
public String getEncode() {
return encode;
}
public Encoder getEncoder() {
return encoder;
}
}

View File

@@ -0,0 +1,329 @@
/**
* 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.Formatter;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.jpacker.encoders.BasicEncoder;
import com.jpacker.encoders.Encoder;
import com.jpacker.strategies.DefaultReplacementStrategy;
import com.jpacker.strategies.ReplacementStrategy;
/**
* Packer class.
*
* Main jPacker class that packs the script.
*
* @author Pablo Santiago <pablo.santiago @ gmail.com>
*/
public class JPackerExecuter {
private JPackerEncoding encoding;
private static final String UNPACK = "eval(function(p,a,c,k,e,r){e=%5$s;if(!''.replace(/^/,String)){while(c--)r[%6$s]=k[c]"
+ "||%6$s;k=[function(e){return r[e]}];e=function(){return'\\\\w+'};c=1};while(c--)if(k[c])p=p."
+ "replace(new RegExp('\\\\b'+e(c)+'\\\\b','g'),k[c]);return p}('%1$s',%2$s,%3$s,'%4$s'.split('|'),0,{}))";
/**
* Constructor
*
* @param encoding
* The encoding level for this instance
*/
public JPackerExecuter(JPackerEncoding encoding) {
setEncoding(encoding);
}
/**
* Packs the script
*
* @param script
* The script to pack
* @param minifyOnly
* True if script should only be minified and not encoded and/or
* its variables shrunk, false otherwise.
* @param shrinkVariables
* True if variables should be shrunk, false otherwise. If
* minifyOnly is true, this option has no side effect.
* @return The packed script
*/
public String pack(String script, boolean minifyOnly, boolean shrinkVariables) {
script += "\n";
script = minify(script);
if (!minifyOnly) {
if (shrinkVariables) {
script = shrinkVariables(script);
}
if (encoding != JPackerEncoding.NONE) {
script = encode(script);
}
}
return script;
}
// zero encoding - just removal of whitespace and comments
private String minify(String script) {
JPackerParser parser = new JPackerParser();
ReplacementStrategy defaultStrat = new DefaultReplacementStrategy();
// protect data
parser = addDataRegEx(parser);
script = parser.exec(script, defaultStrat);
// remove white-space
parser = addWhiteSpaceRegEx(parser);
script = parser.exec(script, defaultStrat);
// clean
parser = addCleanUpRegEx(parser);
script = parser.exec(script, defaultStrat);
// done
return script;
}
private JPackerParser addDataRegEx(JPackerParser parser) {
final String COMMENT1 = "(\\/\\/|;;;)[^\\n]*";
final String COMMENT2 = "\\/\\*[^*]*\\*+([^\\/][^*]*\\*+)*\\/";
final String REGEX = "\\/(\\\\[\\/\\\\]|[^*\\/])(\\\\.|[^\\/\\n\\\\])*\\/[gim]*";
// Packer.CONTINUE
parser.remove("\\\\\\r?\\n");
parser.ignore("'(\\\\.|[^'\\\\])*'");
parser.ignore("\"(\\\\.|[^\"\\\\])*\"");
parser.ignore("\\/\\*@|@\\*\\/|\\/\\/@[^\\n]*\\n");
parser.replace("(" + COMMENT1 + ")\\n\\s*(" + REGEX + ")?", "\n$3");
parser.replace("(" + COMMENT2 + ")\\s*(" + REGEX + ")?", " $3");
parser.replace("([\\[\\(\\^=,{}:;&|!*?])\\s*(" + REGEX + ")", "$1$2");
return parser;
}
private JPackerParser addCleanUpRegEx(JPackerParser parser) {
parser.replace("\\(\\s*;\\s*;\\s*\\)", "(;;)");
parser.ignore("throw[};]+[};]"); // safari 1.3 bug
parser.replace(";+\\s*([};])", "$1");
parser.remove(";;[^\\n\\r]+[\\n\\r]");
return parser;
}
private JPackerParser addWhiteSpaceRegEx(JPackerParser parser) {
parser.replace("(\\d)\\s+(\\.\\s*[a-z\\$_\\[\\(])", "$1 $2");
parser.replace("([+\\-])\\s+([+\\-])", "$1 $2");
parser.replace("(\\b|\\$)\\s+(\\b|\\$)", "$1 $2");
parser.replace("\\b\\s+\\$\\s+\\b", " $ ");
parser.replace("\\$\\s+\\b", "$ ");
parser.replace("\\b\\s+\\$", " $");
parser.replace("\\b\\s+\\b", " ");
parser.remove("\\s+");
return parser;
}
private String shrinkVariables(String script) {
final Pattern pattern = Pattern.compile("^[^'\"]\\/");
// identify blocks, particularly identify function blocks (which define
// scope)
Pattern blockPattern = Pattern.compile("(function\\s*[\\w$]*\\s*\\(\\s*([^\\)]*)\\s*\\)\\s*)?(\\{([^{}]*)\\})");
List<String> blocks = new ArrayList<String>(); // store program blocks
// (anything between
// braces {})
final List<String> data = new ArrayList<String>(); // encoded strings
// and regular
// expressions
JPackerParser parser = new JPackerParser();
parser = addDataRegEx(parser);
script = parser.exec(script, new ReplacementStrategy() {
@Override
public String replace(List<JPackerPattern> patterns, Matcher matcher) {
String replacement = "#" + data.size();
String string = matcher.group();
if (pattern.matcher(string).find()) {
replacement = string.charAt(0) + replacement;
string = string.substring(1);
}
data.add(string);
return replacement;
}
});
do {
// put the blocks back
Matcher blockMatcher = blockPattern.matcher(script);
StringBuffer sb = new StringBuffer();
while (blockMatcher.find()) {
blockMatcher.appendReplacement(sb, encodeBlock(blockMatcher, blocks));
}
blockMatcher.appendTail(sb);
script = sb.toString();
} while (blockPattern.matcher(script).find());
while (Pattern.compile("~(\\d+)~").matcher(script).find()) {
script = decodeBlock(script, blocks);
}
// put strings and regular expressions back
Matcher storeMatcher = Pattern.compile("#(\\d+)").matcher(script);
StringBuffer sb2 = new StringBuffer();
while (storeMatcher.find()) {
int num = Integer.parseInt(storeMatcher.group(1));
storeMatcher.appendReplacement(sb2, Matcher.quoteReplacement(data.get(num)));
}
storeMatcher.appendTail(sb2);
return sb2.toString();
}
private String encode(String script) {
JPackerWords words = new JPackerWords(script, encoding);
Pattern wordsPattern = Pattern.compile("\\w+");
Matcher wordsMatcher = wordsPattern.matcher(script);
StringBuffer sb = new StringBuffer();
while (wordsMatcher.find()) {
JPackerWord tempWord = new JPackerWord(wordsMatcher.group());
wordsMatcher.appendReplacement(sb, words.find(tempWord).getEncoded());
}
wordsMatcher.appendTail(sb);
int ascii = Math.min(Math.max(words.getWords().size(), 2), encoding.getEncodingBase());
String p = escape(sb.toString());
String a = String.valueOf(ascii);
String c = String.valueOf(words.getWords().size());
String k = words.toString();
String e = getEncode(ascii);
String r = ascii > 10 ? "e(c)" : "c";
return new Formatter().format(UNPACK, p, a, c, k, e, r).toString();
}
// encoder for program blocks
private String encodeBlock(Matcher matcher, List<String> blocks) {
String block = matcher.group();
String func = matcher.group(1);
String args = matcher.group(2);
if (func != null && !func.isEmpty()) { // the block is a function block
// decode the function block (THIS IS THE IMPORTANT BIT)
// We are retrieving all sub-blocks and will re-parse them in light
// of newly shrunk variables
while (Pattern.compile("~(\\d+)~").matcher(block).find()) {
block = decodeBlock(block, blocks);
}
// create the list of variable and argument names
Pattern varNamePattern = Pattern.compile("var\\s+[\\w$]+");
Matcher varNameMatcher = varNamePattern.matcher(block);
StringBuilder sb = new StringBuilder();
while (varNameMatcher.find()) {
sb.append(varNameMatcher.group()).append(",");
}
String vars = "";
if (!sb.toString().isEmpty()) {
vars = sb.deleteCharAt(sb.length() - 1).toString().replaceAll("var\\s+", "");
}
String[] ids = concat(args.split("\\s*,\\s*"), vars.split("\\s*,\\s*"));
Set<String> idList = new LinkedHashSet<String>();
for (String s : ids) {
if (!s.isEmpty()) {
idList.add(s);
}
}
// process each identifier
int count = 0;
String shortId;
for (String id : idList) {
id = id.trim();
if (id.length() > 1) { // > 1 char
id = Matcher.quoteReplacement(id);
// find the next free short name (check everything in the
// current scope)
Encoder e = new BasicEncoder();
do {
shortId = e.encode(count++);
} while (Pattern.compile("[^\\w$.]" + shortId + "[^\\w$:]").matcher(block).find());
// replace the long name with the short name
while (Pattern.compile("([^\\w$.])" + id + "([^\\w$:])").matcher(block).find()) {
block = block.replaceAll("([^\\w$.])" + id + "([^\\w$:])", "$1" + shortId + "$2");
}
block = block.replaceAll("([^{,\\w$.])" + id + ":", "$1" + shortId + ":");
}
}
}
String replacement = "~" + blocks.size() + "~";
blocks.add(block);
return replacement;
}
private String decodeBlock(String block, List<String> blocks) {
Matcher encoded = Pattern.compile("~(\\d+)~").matcher(block);
StringBuffer sbe = new StringBuffer();
while (encoded.find()) {
int num = Integer.parseInt(encoded.group(1));
encoded.appendReplacement(sbe, Matcher.quoteReplacement(blocks.get(num)));
}
encoded.appendTail(sbe);
return sbe.toString();
}
private String[] concat(String[] a, String[] b) {
String[] c = new String[a.length + b.length];
System.arraycopy(a, 0, c, 0, a.length);
System.arraycopy(b, 0, c, a.length, b.length);
return c;
}
private String getEncode(int ascii) {
if (ascii > 96) {
return JPackerEncoding.HIGH_ASCII.getEncode();
} else if (ascii > 36) {
return JPackerEncoding.NORMAL.getEncode();
} else if (ascii > 10) {
return JPackerEncoding.MID.getEncode();
} else {
return JPackerEncoding.NUMERIC.getEncode();
}
}
private String escape(String input) {
// single quotes wrap the final string so escape them
// also escape new lines required by conditional comments
return input.replaceAll("([\\\\'])", "\\\\$1").replaceAll("[\\r\\n]+", "\\n");
}
/**
* Encoding level. Options are: {@link JPackerEncoding#NONE},
* {@link JPackerEncoding#NUMERIC}, {@link JPackerEncoding#MID},
* {@link JPackerEncoding#NORMAL} and {@link JPackerEncoding#HIGH_ASCII}.
*
* @return The current encoding level
*/
public JPackerEncoding getEncoding() {
return encoding;
}
/**
* Set the encoding level to use.
*
* @param encoding
*/
public final void setEncoding(JPackerEncoding encoding) {
this.encoding = encoding;
}
}

View File

@@ -0,0 +1,182 @@
/**
* 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;
}
}

View File

@@ -0,0 +1,65 @@
/**
* 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 com.jpacker.evaluators.Evaluator;
/**
* Wrapper class for each pattern
*
* @author Pablo Santiago <pablo.santiago @ gmail.com>
*/
public class JPackerPattern {
private String expression;
private Evaluator evaluator;
private int length;
public JPackerPattern() {
}
public JPackerPattern(String expression, Evaluator evaluator) {
this.expression = expression;
this.evaluator = evaluator;
evaluator.setJPattern(this);
}
public String getExpression() {
return expression;
}
public void setExpression(String expression) {
this.expression = expression;
}
public int getLength() {
return length;
}
public void setLength(int length) {
this.length = length;
}
public Evaluator getEvaluator() {
return evaluator;
}
public void setEvaluator(Evaluator evaluator) {
this.evaluator = evaluator;
}
@Override
public String toString() {
return "(" + expression + ")";
}
}

View File

@@ -0,0 +1,98 @@
/**
* 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;
/**
* Wrapper class for a keyword
*
* @author Pablo Santiago <pablo.santiago @ gmail.com>
*/
public class JPackerWord {
private int count = 0;
private String encoded = "";
private int index = -1;
private String word;
private String replacement;
public JPackerWord(String word) {
this.word = word;
}
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
public String getEncoded() {
return encoded;
}
public void setEncoded(String encoded) {
this.encoded = encoded;
}
public int getIndex() {
return index;
}
public void setIndex(int index) {
this.index = index;
}
public String getWord() {
return word;
}
public void setWord(String word) {
this.word = word;
}
public String getReplacement() {
return replacement;
}
public void setReplacement(String replacement) {
this.replacement = replacement;
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final JPackerWord other = (JPackerWord) obj;
if ((this.word == null) ? (other.word != null) : !this.word.equals(other.word)) {
return false;
}
return true;
}
@Override
public int hashCode() {
int hash = 3;
hash = 37 * hash + (this.word != null ? this.word.hashCode() : 0);
return hash;
}
@Override
public String toString() {
return word;
}
}

View File

@@ -0,0 +1,149 @@
/**
* 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.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Wrapper class for a {@link JPackerWord} list built based on script's keywords (later
* wrapped into a JPackerWord list)
*
* @author Pablo Santiago <pablo.santiago @ gmail.com>
*/
public final class JPackerWords {
private JPackerEncoding encoding;
private static final Pattern WORDS = Pattern.compile("\\w+");
private List<JPackerWord> words = new ArrayList<JPackerWord>();
/**
* Constructor
*
* @param script
* The input script to look up for keywords
* @param encoding
* The encoding level to use
*/
public JPackerWords(String script, JPackerEncoding encoding) {
this.encoding = encoding;
Matcher matcher = WORDS.matcher(script);
while (matcher.find()) {
add(new JPackerWord(matcher.group()));
}
encode();
}
private void add(JPackerWord word) {
if (!words.contains(word)) {
words.add(word);
}
JPackerWord w = find(word);
w.setCount(w.getCount() + 1);
}
private void encode() {
// sort by frequency
Collections.sort(words, new Comparator<JPackerWord>() {
@Override
public int compare(JPackerWord x, JPackerWord y) {
return y.getCount() - x.getCount();
}
});
// a dictionary of encoding base -> base10
Map<String, Integer> encoded = new HashMap<String, Integer>();
for (int i = 0; i < words.size(); i++) {
encoded.put(encoding.getEncoder().encode(i), i);
}
int index = 0;
for (JPackerWord word : words) {
if (encoded.containsKey(word.getWord())) {
word.setIndex(encoded.get(word.getWord()));
word.setReplacement("");
} else {
while (words.contains(new JPackerWord(encoding.getEncoder().encode(index)))) {
index++;
}
word.setIndex(index++);
word.setReplacement(word.getWord());
}
word.setEncoded(encoding.getEncoder().encode(word.getIndex()));
}
// sort by encoding
Collections.sort(words, new Comparator<JPackerWord>() {
@Override
public int compare(JPackerWord x, JPackerWord y) {
return x.getIndex() - y.getIndex();
}
});
}
/**
* Find a word in the JPackerWord list
*
* @param word
* The JPackerWord object to find in the list
* @return The JPackerWord object if found, null otherwise
*/
public JPackerWord find(JPackerWord word) {
Iterator<JPackerWord> it = words.iterator();
while (it.hasNext() == true) {
JPackerWord pw = it.next();
if (pw.equals(word)) {
return pw;
}
}
return null;
}
/**
* Gets the list of JPackerWord objects
*
* @return The list of JPackerWord objects
*/
public List<JPackerWord> getWords() {
return words;
}
/**
* This method has been overridden to return the list of JPackerWord objects
* as a single String object separated by the '|' character
*
* @return A List of JPackerWord objects as a single String object separated
* by the '|' character
*/
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
for (JPackerWord word : words) {
sb.append(word.getReplacement()).append('|');
}
sb.deleteCharAt(sb.length() - 1);
return sb.toString();
}
}

View File

@@ -0,0 +1,27 @@
/**
* 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.encoders;
/**
* Basic (base52) encoder: [a-Z]
*
* @author Pablo Santiago <pablo.santiago @ gmail.com>
*
*/
public class BasicEncoder implements Encoder {
@Override
public String encode(int c) {
return (c < 52 ? "" : encode(c / 52)) + (((c = c % 52)) > 25 ? String.valueOf((char) (c + 39)) : String.valueOf((char) (c + 97)));
}
}

View File

@@ -0,0 +1,23 @@
/**
* 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.encoders;
/**
* Encoder interface to build encoder objects of different base
*
* @author Pablo Santiago <pablo.santiago @ gmail.com>
*/
public interface Encoder {
public String encode(int code);
}

View File

@@ -0,0 +1,36 @@
/**
* 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.encoders;
/**
* High-Ascii (base95) encoder: [¡-ÿ] SHOULD BE USED WITH CAUTION! Not fully
* tested.
*
* @author Pablo Santiago <pablo.santiago @ gmail.com>
*/
public class HighAsciiEncoder implements Encoder {
private static String LOOKUP_95 = "¡¢£€¥Š§š©ª«¬­®¯°±²³Žµ¶·ž¹º»ŒœŸ¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ";
@Override
public String encode(int code) {
String encoded = "";
int i = 0;
do {
int digit = (code / (int) Math.pow(95, i)) % 95;
encoded = LOOKUP_95.charAt(digit) + encoded;
code -= digit * (int) Math.pow(95, i++);
} while (code > 0);
return encoded;
}
}

View File

@@ -0,0 +1,38 @@
/**
* 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.encoders;
/**
* Mid base36 encoder: [0-z]
*
* @author Pablo Santiago <pablo.santiago @ gmail.com>
*
*/
public class MidEncoder implements Encoder {
// lookups seemed like the easiest way to do this since
// I don't know of an equivalent to .toString(36)
private static String LOOKUP_36 = "0123456789abcdefghijklmnopqrstuvwxyz";
@Override
public String encode(int code) {
String encoded = "";
int i = 0;
do {
int digit = (code / (int) Math.pow(36, i)) % 36;
encoded = LOOKUP_36.charAt(digit) + encoded;
code -= digit * (int) Math.pow(36, i++);
} while (code > 0);
return encoded;
}
}

View File

@@ -0,0 +1,27 @@
/**
* 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.encoders;
/**
* Normal (base62) encoder: [0-Z]
*
* @author Pablo Santiago <pablo.santiago @ gmail.com>
*
*/
public class NormalEncoder implements Encoder {
@Override
public String encode(int c) {
return (c < 62 ? "" : encode(c / 62)) + ((c = c % 62) > 35 ? String.valueOf((char) (c + 29)) : Integer.toString(c, 36));
}
}

View File

@@ -0,0 +1,27 @@
/**
* 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.encoders;
/**
* Numeric (base10) encoder: [0-9]
*
* @author Pablo Santiago <pablo.santiago @ gmail.com>
*
*/
public class NumericEncoder implements Encoder {
@Override
public String encode(int code) {
return String.valueOf(code);
}
}

View File

@@ -0,0 +1,48 @@
/**
* 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.evaluators;
import com.jpacker.JPackerPattern;
/**
* Abstract class for {@link Evaluator} objects. Its purpose is to provide the
* implementation of a getter and setter of a {@link JPackerPattern} object.
*
* @author Pablo Santiago <pablo.santiago @ gmail.com>
*/
public abstract class AbstractEvaluator implements Evaluator {
private JPackerPattern jpattern;
/**
* Gets the {@link JPackerPattern} object
*
* @return The {@link JPackerPattern} object
*/
@Override
public JPackerPattern getJPattern() {
return jpattern;
}
/**
* Sets the {@link JPackerPattern} object for this {@link Evaluator} object
*
* @param jpattern
* The {@link JPackerPattern} object to set
*/
@Override
public void setJPattern(JPackerPattern jpattern) {
this.jpattern = jpattern;
}
}

View File

@@ -0,0 +1,29 @@
/**
* 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.evaluators;
import java.util.regex.Matcher;
/**
* An {@link Evaluator} implementation to have a {@link String} matched by an
* expression removed from the script
*
* @author Pablo Santiago <pablo.santiago @ gmail.com>
*/
public class DeleteEvaluator extends AbstractEvaluator implements Evaluator {
@Override
public String evaluate(Matcher matcher, int offset) {
return "\u0001" + matcher.group(offset) + "\u0001";
}
}

View File

@@ -0,0 +1,68 @@
/**
* 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>
* Email: <pablo.santiago @ gmail.com>
*/
package com.jpacker.evaluators;
import java.util.regex.Matcher;
import com.jpacker.JPackerParser;
import com.jpacker.JPackerPattern;
import com.jpacker.strategies.DefaultReplacementStrategy;
/**
* After expressions have been added to a {@link JPackerParser} object and a
* ReplacementStrategy has been set, each {@link JPackerPattern} should have an
* {@link Evaluator} object that'll evaluate a certain match of a pattern
* expression and return a suitable replacement String. Commonly, after all
* expressions have been added the the {@link JPackerParser} object, a one-line
* String that contains all expressions is created by the {@link JPackerParser}
* object using the {@link JPackerParser#getJPatterns()} method. In the
* {@link #evaluate(Matcher, int)} method, the offset integer parameter
* represents the position in such String that corresponds to a
* {@link JPackerPattern} object.
*
* @author Pablo Santiago <pablo.santiago @ gmail.com>
*/
public interface Evaluator {
/**
* Sets the {@link JPackerPattern} object to use
*
* @param jpattern
* The {@link JPackerPattern} object for the {@link Evaluator}
* implementations to use
*/
public void setJPattern(JPackerPattern jpattern);
/**
* Gets the {@link JPackerPattern} object
*
* @return The {@link JPackerPattern} object if it has been set, null
* otherwise
*/
public JPackerPattern getJPattern();
/**
* Evaluates the string matched by the {@link Matcher} object and returns a
* suitable replacement String.
*
* @param matcher
* The {@link Matcher} object that contains a match (and its
* groups)
* @param offset
* The offset in the String object returned by the
* {@link JPackerParser#getJPatterns()} method in the
* {@link JPackerParser} object
* @return A replacement string (either text or group expressions, i.e.: $1)
* @see DefaultReplacementStrategy
*/
public String evaluate(Matcher matcher, int offset);
}

View File

@@ -0,0 +1,43 @@
/**
* 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.evaluators;
import java.util.regex.Matcher;
/**
* An {@link Evaluator} implementation for replacement {@link String} objects
* such as "$1" or "$2". Must be a simple group reference (i.e.: NOT
* "Hello $3 $2")
*
* @author Pablo Santiago <pablo.santiago @ gmail.com>
*/
public class IntegerEvaluator extends AbstractEvaluator implements Evaluator {
private int replacement;
/**
* Constructor
*
* @param replacement
* If replacement String is "$1" then 1 would be the replacement
* parameter
*/
public IntegerEvaluator(int replacement) {
this.replacement = replacement;
}
@Override
public String evaluate(Matcher matcher, int offset) {
return matcher.group(replacement + offset);
}
}

View File

@@ -0,0 +1,39 @@
/**
* 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.evaluators;
import java.util.regex.Matcher;
public class StringEvaluator extends AbstractEvaluator implements Evaluator {
private String replacement;
public StringEvaluator(String replacement) {
this.replacement = replacement;
}
/**
* Replacement function for complicated lookups (e.g. Hello $3 $2)
*
*/
@Override
public String evaluate(Matcher matcher, int offset) {
int length = getJPattern().getLength();
String result = replacement;
while (length-- > 0) {
String mg = matcher.group(offset + length);
result = result.replace("$" + length, mg == null ? "" : mg);
}
return result;
}
}

View File

@@ -0,0 +1,29 @@
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package com.jpacker.exceptions;
import java.io.IOException;
/**
*
* @author poly
*/
public class EmptyFileException extends IOException {
public EmptyFileException(){
super();
}
public EmptyFileException(String message){
super(message);
}
private EmptyFileException(String path, String reason) {
super(path + ((reason == null)
? ""
: " (" + reason + ")"));
}
}

View File

@@ -0,0 +1,57 @@
/**
* 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.strategies;
import java.util.List;
import java.util.regex.Matcher;
import com.jpacker.JPackerPattern;
/**
* Default replacement strategy class.
*
* @author Pablo Santiago <pablo.santiago @ gmail.com>
*/
public class DefaultReplacementStrategy implements ReplacementStrategy {
/**
* Default replacement function. Called once for each match found
*
* @param jpatterns
* A List<JPackerPattern> that contains all
* {@link JPackerPattern} objects that wrap expressions to be
* evaluated
* @param matcher
* A {@link Matcher} object that corresponds to a match in the
* script
*/
@Override
public String replace(List<JPackerPattern> jpatterns, Matcher matcher) {
int i = 1;
// loop through the patterns
for (JPackerPattern jpattern : jpatterns) {
// do we have a result?
if (isMatch(matcher.group(i))) {
return jpattern.getEvaluator().evaluate(matcher, i);
} else { // skip over references to sub-expressions
i += jpattern.getLength();
}
}
return matcher.group(); // should never be hit, but you never know
}
// check that match is not an empty string
private boolean isMatch(String match) {
return match != null && !match.isEmpty();
}
}

View File

@@ -0,0 +1,38 @@
/**
* 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>
* Email: <pablo.santiago @ gmail.com>
*/
package com.jpacker.strategies;
import java.util.List;
import java.util.regex.Matcher;
import com.jpacker.JPackerPattern;
/**
* An interface to build replacement strategies
*
* @author Pablo Santiago <pablo.santiago @ gmail.com>
*/
public interface ReplacementStrategy {
/**
* Replacement function. Called once for each match found
*
* @param patterns
* A List<JPackerPattern> that contains all
* {@link JPackerPattern} objects that wrap expressions to be
* evaluated
* @param matcher
* A {@link Matcher} object that corresponds to a match in the
* script
*/
public String replace(List<JPackerPattern> patterns, Matcher matcher);
}