* finish rewrite of ExpressionFormat customizations
This commit is contained in:
parent
3339dc36d1
commit
1a730c3ec6
|
@ -1,13 +1,11 @@
|
||||||
|
|
||||||
package net.sourceforge.filebot.format;
|
package net.sourceforge.filebot.format;
|
||||||
|
|
||||||
|
|
||||||
import static net.sourceforge.filebot.util.ExceptionUtilities.*;
|
import static net.sourceforge.filebot.util.ExceptionUtilities.*;
|
||||||
import static net.sourceforge.filebot.util.FileUtilities.*;
|
import static net.sourceforge.filebot.util.FileUtilities.*;
|
||||||
|
import groovy.lang.GroovyClassLoader;
|
||||||
import groovy.lang.GroovyRuntimeException;
|
import groovy.lang.GroovyRuntimeException;
|
||||||
import groovy.lang.MissingPropertyException;
|
import groovy.lang.MissingPropertyException;
|
||||||
|
|
||||||
import java.io.InputStreamReader;
|
|
||||||
import java.security.AccessController;
|
import java.security.AccessController;
|
||||||
import java.text.FieldPosition;
|
import java.text.FieldPosition;
|
||||||
import java.text.Format;
|
import java.text.Format;
|
||||||
|
@ -25,25 +23,35 @@ import javax.script.ScriptEngine;
|
||||||
import javax.script.ScriptException;
|
import javax.script.ScriptException;
|
||||||
import javax.script.SimpleScriptContext;
|
import javax.script.SimpleScriptContext;
|
||||||
|
|
||||||
|
import org.codehaus.groovy.control.CompilerConfiguration;
|
||||||
import org.codehaus.groovy.control.MultipleCompilationErrorsException;
|
import org.codehaus.groovy.control.MultipleCompilationErrorsException;
|
||||||
import org.codehaus.groovy.jsr223.GroovyScriptEngineFactory;
|
import org.codehaus.groovy.control.customizers.ImportCustomizer;
|
||||||
|
import org.codehaus.groovy.jsr223.GroovyScriptEngineImpl;
|
||||||
|
|
||||||
public class ExpressionFormat extends Format {
|
public class ExpressionFormat extends Format {
|
||||||
|
|
||||||
private static ScriptEngine engine;
|
private static ScriptEngine engine;
|
||||||
private static Map<String, CompiledScript> scriptletCache = new HashMap<String, CompiledScript>();
|
private static Map<String, CompiledScript> scriptletCache = new HashMap<String, CompiledScript>();
|
||||||
|
|
||||||
|
protected static ScriptEngine createScriptEngine() {
|
||||||
|
CompilerConfiguration config = new CompilerConfiguration();
|
||||||
|
|
||||||
|
// include default functions
|
||||||
|
ImportCustomizer imports = new ImportCustomizer();
|
||||||
|
imports.addStaticStars(ExpressionFormatFunctions.class.getName());
|
||||||
|
config.addCompilationCustomizers(imports);
|
||||||
|
|
||||||
|
GroovyClassLoader classLoader = new GroovyClassLoader(Thread.currentThread().getContextClassLoader(), config);
|
||||||
|
return new GroovyScriptEngineImpl(classLoader);
|
||||||
|
}
|
||||||
|
|
||||||
protected static synchronized ScriptEngine getGroovyScriptEngine() throws ScriptException {
|
protected static synchronized ScriptEngine getGroovyScriptEngine() throws ScriptException {
|
||||||
if (engine == null) {
|
if (engine == null) {
|
||||||
engine = new GroovyScriptEngineFactory().getScriptEngine();
|
engine = createScriptEngine();
|
||||||
engine.eval(new InputStreamReader(ExpressionFormat.class.getResourceAsStream("ExpressionFormat.lib.groovy")));
|
|
||||||
}
|
}
|
||||||
return engine;
|
return engine;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected static synchronized CompiledScript compileScriptlet(String expression) throws ScriptException {
|
protected static synchronized CompiledScript compileScriptlet(String expression) throws ScriptException {
|
||||||
Compilable engine = (Compilable) getGroovyScriptEngine();
|
Compilable engine = (Compilable) getGroovyScriptEngine();
|
||||||
CompiledScript scriptlet = scriptletCache.get(expression);
|
CompiledScript scriptlet = scriptletCache.get(expression);
|
||||||
|
@ -53,38 +61,35 @@ public class ExpressionFormat extends Format {
|
||||||
}
|
}
|
||||||
return scriptlet;
|
return scriptlet;
|
||||||
}
|
}
|
||||||
|
|
||||||
private final String expression;
|
private final String expression;
|
||||||
|
|
||||||
private final Object[] compilation;
|
private final Object[] compilation;
|
||||||
|
|
||||||
private ScriptException lastException;
|
private ScriptException lastException;
|
||||||
|
|
||||||
|
|
||||||
public ExpressionFormat(String expression) throws ScriptException {
|
public ExpressionFormat(String expression) throws ScriptException {
|
||||||
this.expression = expression;
|
this.expression = expression;
|
||||||
this.compilation = secure(compile(expression));
|
this.compilation = secure(compile(expression));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public String getExpression() {
|
public String getExpression() {
|
||||||
return expression;
|
return expression;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected Object[] compile(String expression) throws ScriptException {
|
protected Object[] compile(String expression) throws ScriptException {
|
||||||
List<Object> compilation = new ArrayList<Object>();
|
List<Object> compilation = new ArrayList<Object>();
|
||||||
|
|
||||||
char open = '{';
|
char open = '{';
|
||||||
char close = '}';
|
char close = '}';
|
||||||
|
|
||||||
StringBuilder token = new StringBuilder();
|
StringBuilder token = new StringBuilder();
|
||||||
int level = 0;
|
int level = 0;
|
||||||
|
|
||||||
// parse expressions and literals
|
// parse expressions and literals
|
||||||
for (int i = 0; i < expression.length(); i++) {
|
for (int i = 0; i < expression.length(); i++) {
|
||||||
char c = expression.charAt(i);
|
char c = expression.charAt(i);
|
||||||
|
|
||||||
if (c == open) {
|
if (c == open) {
|
||||||
if (level == 0) {
|
if (level == 0) {
|
||||||
if (token.length() > 0) {
|
if (token.length() > 0) {
|
||||||
|
@ -94,7 +99,7 @@ public class ExpressionFormat extends Format {
|
||||||
} else {
|
} else {
|
||||||
token.append(c);
|
token.append(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
level++;
|
level++;
|
||||||
} else if (c == close) {
|
} else if (c == close) {
|
||||||
if (level == 1) {
|
if (level == 1) {
|
||||||
|
@ -104,14 +109,14 @@ public class ExpressionFormat extends Format {
|
||||||
} catch (ScriptException e) {
|
} catch (ScriptException e) {
|
||||||
// try to extract syntax exception
|
// try to extract syntax exception
|
||||||
ScriptException illegalSyntax = e;
|
ScriptException illegalSyntax = e;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
String message = findCause(e, MultipleCompilationErrorsException.class).getErrorCollector().getSyntaxError(0).getOriginalMessage();
|
String message = findCause(e, MultipleCompilationErrorsException.class).getErrorCollector().getSyntaxError(0).getOriginalMessage();
|
||||||
illegalSyntax = new ScriptException("SyntaxError: " + message);
|
illegalSyntax = new ScriptException("SyntaxError: " + message);
|
||||||
} catch (Exception ignore) {
|
} catch (Exception ignore) {
|
||||||
// ignore, just use original exception
|
// ignore, just use original exception
|
||||||
}
|
}
|
||||||
|
|
||||||
throw illegalSyntax;
|
throw illegalSyntax;
|
||||||
} finally {
|
} finally {
|
||||||
token.setLength(0);
|
token.setLength(0);
|
||||||
|
@ -120,65 +125,62 @@ public class ExpressionFormat extends Format {
|
||||||
} else {
|
} else {
|
||||||
token.append(c);
|
token.append(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
level--;
|
level--;
|
||||||
} else {
|
} else {
|
||||||
token.append(c);
|
token.append(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
// sanity check
|
// sanity check
|
||||||
if (level < 0) {
|
if (level < 0) {
|
||||||
throw new ScriptException("SyntaxError: unexpected token: " + close);
|
throw new ScriptException("SyntaxError: unexpected token: " + close);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// sanity check
|
// sanity check
|
||||||
if (level != 0) {
|
if (level != 0) {
|
||||||
throw new ScriptException("SyntaxError: missing token: " + close);
|
throw new ScriptException("SyntaxError: missing token: " + close);
|
||||||
}
|
}
|
||||||
|
|
||||||
// append tail
|
// append tail
|
||||||
if (token.length() > 0) {
|
if (token.length() > 0) {
|
||||||
compilation.add(token.toString());
|
compilation.add(token.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
return compilation.toArray();
|
return compilation.toArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public Bindings getBindings(Object value) {
|
public Bindings getBindings(Object value) {
|
||||||
return new ExpressionBindings(value) {
|
return new ExpressionBindings(value) {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object get(Object key) {
|
public Object get(Object key) {
|
||||||
return normalizeBindingValue(super.get(key));
|
return normalizeBindingValue(super.get(key));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public StringBuffer format(Object object, StringBuffer sb, FieldPosition pos) {
|
public StringBuffer format(Object object, StringBuffer sb, FieldPosition pos) {
|
||||||
return format(getBindings(object), sb);
|
return format(getBindings(object), sb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public StringBuffer format(Bindings bindings, StringBuffer sb) {
|
public StringBuffer format(Bindings bindings, StringBuffer sb) {
|
||||||
// use privileged bindings so we are not restricted by the script sandbox
|
// use privileged bindings so we are not restricted by the script sandbox
|
||||||
Bindings priviledgedBindings = PrivilegedInvocation.newProxy(Bindings.class, bindings, AccessController.getContext());
|
Bindings priviledgedBindings = PrivilegedInvocation.newProxy(Bindings.class, bindings, AccessController.getContext());
|
||||||
|
|
||||||
// initialize script context with the privileged bindings
|
// initialize script context with the privileged bindings
|
||||||
ScriptContext context = new SimpleScriptContext();
|
ScriptContext context = new SimpleScriptContext();
|
||||||
context.setBindings(priviledgedBindings, ScriptContext.GLOBAL_SCOPE);
|
context.setBindings(priviledgedBindings, ScriptContext.GLOBAL_SCOPE);
|
||||||
|
|
||||||
// reset exception state
|
// reset exception state
|
||||||
lastException = null;
|
lastException = null;
|
||||||
|
|
||||||
for (Object snipped : compilation) {
|
for (Object snipped : compilation) {
|
||||||
if (snipped instanceof CompiledScript) {
|
if (snipped instanceof CompiledScript) {
|
||||||
try {
|
try {
|
||||||
Object value = normalizeExpressionValue(((CompiledScript) snipped).eval(context));
|
Object value = normalizeExpressionValue(((CompiledScript) snipped).eval(context));
|
||||||
|
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
sb.append(value);
|
sb.append(value);
|
||||||
}
|
}
|
||||||
|
@ -189,27 +191,24 @@ public class ExpressionFormat extends Format {
|
||||||
sb.append(snipped);
|
sb.append(snipped);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return sb;
|
return sb;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected Object normalizeBindingValue(Object value) {
|
protected Object normalizeBindingValue(Object value) {
|
||||||
// if the binding value is a String, remove illegal characters
|
// if the binding value is a String, remove illegal characters
|
||||||
if (value instanceof CharSequence) {
|
if (value instanceof CharSequence) {
|
||||||
return replacePathSeparators(value.toString()).trim();
|
return replacePathSeparators(value.toString()).trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
// if the binding value is an Object, just leave it
|
// if the binding value is an Object, just leave it
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected Object normalizeExpressionValue(Object value) {
|
protected Object normalizeExpressionValue(Object value) {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected void handleException(ScriptException exception) {
|
protected void handleException(ScriptException exception) {
|
||||||
if (findCause(exception, MissingPropertyException.class) != null) {
|
if (findCause(exception, MissingPropertyException.class) != null) {
|
||||||
lastException = new ExpressionException(new BindingException(findCause(exception, MissingPropertyException.class).getProperty(), "undefined", exception));
|
lastException = new ExpressionException(new BindingException(findCause(exception, MissingPropertyException.class).getProperty(), "undefined", exception));
|
||||||
|
@ -219,29 +218,26 @@ public class ExpressionFormat extends Format {
|
||||||
lastException = exception;
|
lastException = exception;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public ScriptException caughtScriptException() {
|
public ScriptException caughtScriptException() {
|
||||||
return lastException;
|
return lastException;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private Object[] secure(Object[] compilation) {
|
private Object[] secure(Object[] compilation) {
|
||||||
for (int i = 0; i < compilation.length; i++) {
|
for (int i = 0; i < compilation.length; i++) {
|
||||||
Object snipped = compilation[i];
|
Object snipped = compilation[i];
|
||||||
|
|
||||||
if (snipped instanceof CompiledScript) {
|
if (snipped instanceof CompiledScript) {
|
||||||
compilation[i] = new SecureCompiledScript((CompiledScript) snipped);
|
compilation[i] = new SecureCompiledScript((CompiledScript) snipped);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return compilation;
|
return compilation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object parseObject(String source, ParsePosition pos) {
|
public Object parseObject(String source, ParsePosition pos) {
|
||||||
throw new UnsupportedOperationException();
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,55 @@
|
||||||
|
package net.sourceforge.filebot.format;
|
||||||
|
|
||||||
|
import groovy.lang.Closure;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Global functions available in the {@link ExpressionFormat}
|
||||||
|
*/
|
||||||
|
public class ExpressionFormatFunctions {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* General helpers and utilities
|
||||||
|
*/
|
||||||
|
public static Object c(Closure<?> c) {
|
||||||
|
try {
|
||||||
|
return c.call();
|
||||||
|
} catch (Exception e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Object any(Closure<?>... closures) {
|
||||||
|
for (Closure<?> it : closures) {
|
||||||
|
try {
|
||||||
|
Object result = it.call();
|
||||||
|
if (result != null) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<Object> allOf(Closure<?>... closures) {
|
||||||
|
List<Object> values = new ArrayList<Object>();
|
||||||
|
|
||||||
|
for (Closure<?> it : closures) {
|
||||||
|
try {
|
||||||
|
Object result = it.call();
|
||||||
|
if (result != null) {
|
||||||
|
values.add(result);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,7 +1,21 @@
|
||||||
package net.sourceforge.filebot.format;
|
package net.sourceforge.filebot.format;
|
||||||
|
|
||||||
|
import static java.util.regex.Pattern.*;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
|
||||||
|
import net.sourceforge.filebot.util.FileUtilities;
|
||||||
|
|
||||||
|
import com.ibm.icu.text.Transliterator;
|
||||||
|
|
||||||
public class ExpressionFormatMethods {
|
public class ExpressionFormatMethods {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenience methods for String.toLowerCase() and String.toUpperCase()
|
||||||
|
*/
|
||||||
public static String lower(String self) {
|
public static String lower(String self) {
|
||||||
return self.toLowerCase();
|
return self.toLowerCase();
|
||||||
}
|
}
|
||||||
|
@ -10,6 +24,11 @@ public class ExpressionFormatMethods {
|
||||||
return self.toUpperCase();
|
return self.toUpperCase();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pad strings or numbers with given characters ('0' by default).
|
||||||
|
*
|
||||||
|
* e.g. "1" -> "01"
|
||||||
|
*/
|
||||||
public static String pad(String self, int length, String padding) {
|
public static String pad(String self, int length, String padding) {
|
||||||
while (self.length() < length) {
|
while (self.length() < length) {
|
||||||
self = padding + self;
|
self = padding + self;
|
||||||
|
@ -22,6 +41,241 @@ public class ExpressionFormatMethods {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String pad(Number self, int length) {
|
public static String pad(Number self, int length) {
|
||||||
return pad(self.toString(), length);
|
return pad(self.toString(), length, "0");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a substring matching the given pattern or break.
|
||||||
|
*/
|
||||||
|
public static String match(String self, String pattern) {
|
||||||
|
return match(self, pattern, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String match(String self, String pattern, int matchGroup) {
|
||||||
|
Matcher matcher = compile(pattern, CASE_INSENSITIVE | UNICODE_CASE | MULTILINE).matcher(self);
|
||||||
|
if (matcher.find()) {
|
||||||
|
return (matcher.groupCount() > 0 && matchGroup < 0 ? matcher.group(1) : matcher.group(matchGroup < 0 ? 0 : matchGroup)).trim();
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException("Pattern not found");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a list of all matching patterns or break.
|
||||||
|
*/
|
||||||
|
public static List<String> matchAll(String self, String pattern) {
|
||||||
|
return matchAll(self, pattern, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<String> matchAll(String self, String pattern, int matchGroup) {
|
||||||
|
List<String> matches = new ArrayList<String>();
|
||||||
|
Matcher matcher = compile(pattern, CASE_INSENSITIVE | UNICODE_CASE | MULTILINE).matcher(self);
|
||||||
|
while (matcher.find()) {
|
||||||
|
matches.add(matcher.group(matchGroup).trim());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (matches.size() > 0) {
|
||||||
|
return matches;
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException("Pattern not found");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String removeAll(String self, String pattern) {
|
||||||
|
return compile(pattern, CASE_INSENSITIVE | UNICODE_CASE | MULTILINE).matcher(self).replaceAll("").trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replace space characters with a given characters.
|
||||||
|
*
|
||||||
|
* e.g. "Doctor Who" -> "Doctor_Who"
|
||||||
|
*/
|
||||||
|
public static String space(String self, String replacement) {
|
||||||
|
return self.replaceAll("[:?._]", " ").trim().replaceAll("\\s+", replacement);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Upper-case all initials.
|
||||||
|
*
|
||||||
|
* e.g. "The Day a new Demon was born" -> "The Day A New Demon Was Born"
|
||||||
|
*/
|
||||||
|
public static String upperInitial(String self) {
|
||||||
|
Matcher matcher = compile("(?<=[&()+.,-;<=>?\\[\\]_{|}~ ]|^)[a-z]").matcher(self);
|
||||||
|
|
||||||
|
StringBuffer buffer = new StringBuffer();
|
||||||
|
while (matcher.find()) {
|
||||||
|
matcher.appendReplacement(buffer, matcher.group().toUpperCase());
|
||||||
|
}
|
||||||
|
matcher.appendTail(buffer);
|
||||||
|
|
||||||
|
return buffer.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String sortName(String self) {
|
||||||
|
return sortName(self, "$2, $1");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String sortName(String self, String replacement) {
|
||||||
|
return compile("^(The|A|An)\\s(.+)", CASE_INSENSITIVE).matcher(self).replaceFirst(replacement).trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get acronym, i.e. first letter of each word.
|
||||||
|
*
|
||||||
|
* e.g. "Deep Space 9" -> "DS9"
|
||||||
|
*/
|
||||||
|
public static String acronym(String self) {
|
||||||
|
String name = sortName(self, "$2");
|
||||||
|
Matcher matcher = compile("(?<=[&()+.,-;<=>?\\[\\]_{|}~ ]|^)[\\p{Alnum}]").matcher(name);
|
||||||
|
|
||||||
|
StringBuilder buffer = new StringBuilder();
|
||||||
|
while (matcher.find()) {
|
||||||
|
buffer.append(matcher.group().toUpperCase());
|
||||||
|
}
|
||||||
|
|
||||||
|
return buffer.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lower-case all letters that are not initials.
|
||||||
|
*
|
||||||
|
* e.g. "Gundam SEED" -> "Gundam Seed"
|
||||||
|
*/
|
||||||
|
public static String lowerTrail(String self) {
|
||||||
|
Matcher matcher = compile("\\b(\\p{Alpha})(\\p{Alpha}+)\\b").matcher(self);
|
||||||
|
|
||||||
|
StringBuffer buffer = new StringBuffer();
|
||||||
|
while (matcher.find()) {
|
||||||
|
matcher.appendReplacement(buffer, matcher.group(1) + matcher.group(2).toLowerCase());
|
||||||
|
}
|
||||||
|
matcher.appendTail(buffer);
|
||||||
|
|
||||||
|
return buffer.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return substring before the given pattern.
|
||||||
|
*/
|
||||||
|
public static String before(String self, String pattern) {
|
||||||
|
Matcher matcher = compile(pattern, CASE_INSENSITIVE | UNICODE_CASE).matcher(self);
|
||||||
|
|
||||||
|
// pattern was found, return leading substring, else return original value
|
||||||
|
return matcher.find() ? self.substring(0, matcher.start()).trim() : self;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return substring after the given pattern.
|
||||||
|
*/
|
||||||
|
public static String after(String self, String pattern) {
|
||||||
|
Matcher matcher = compile(pattern, CASE_INSENSITIVE | UNICODE_CASE).matcher(self);
|
||||||
|
|
||||||
|
// pattern was found, return trailing substring, else return original value
|
||||||
|
return matcher.find() ? self.substring(matcher.end(), self.length()).trim() : self;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replace trailing parenthesis including any leading whitespace.
|
||||||
|
*
|
||||||
|
* e.g. "The IT Crowd (UK)" -> "The IT Crowd"
|
||||||
|
*/
|
||||||
|
public static String replaceTrailingBrackets(String self) {
|
||||||
|
return replaceTrailingBrackets(self, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String replaceTrailingBrackets(String self, String replacement) {
|
||||||
|
return self.replaceAll("\\s*[(]([^)]*)[)]$", replacement).trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replace 'part identifier'.
|
||||||
|
*
|
||||||
|
* e.g. "Today Is the Day: Part 1" -> "Today Is the Day, Part 1" or "Today Is the Day (1)" -> "Today Is the Day, Part 1"
|
||||||
|
*/
|
||||||
|
public static String replacePart(String self) {
|
||||||
|
return replacePart(self, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String replacePart(String self, String replacement) {
|
||||||
|
// handle '(n)', '(Part n)' and ': Part n' like syntax
|
||||||
|
String[] patterns = new String[] { "\\s*[(](\\w+)[)]$", "\\W+Part (\\w+)\\W*$" };
|
||||||
|
|
||||||
|
for (String pattern : patterns) {
|
||||||
|
Matcher matcher = compile(pattern, CASE_INSENSITIVE).matcher(self);
|
||||||
|
if (matcher.find()) {
|
||||||
|
return matcher.replaceAll(replacement).trim();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// no pattern matches, nothing to replace
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply ICU transliteration
|
||||||
|
*
|
||||||
|
* @see http://userguide.icu-project.org/transforms/general
|
||||||
|
*/
|
||||||
|
public static String transliterate(String self, String transformIdentifier) {
|
||||||
|
return Transliterator.getInstance(transformIdentifier).transform(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert Unicode to ASCII as best as possible. Works with most alphabets/scripts used in the world.
|
||||||
|
*
|
||||||
|
* e.g. "Österreich" -> "Osterreich" "カタカナ" -> "katakana"
|
||||||
|
*/
|
||||||
|
public static String ascii(String self) {
|
||||||
|
return ascii(self, " ");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String ascii(String self, String fallback) {
|
||||||
|
return Transliterator.getInstance("Any-Latin;Latin-ASCII;[:Diacritic:]remove").transform(self).replaceAll("[^\\p{ASCII}]+", fallback).trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replace multiple replacement pairs
|
||||||
|
*
|
||||||
|
* e.g. replace('ä', 'ae', 'ö', 'oe', 'ü', 'ue')
|
||||||
|
*/
|
||||||
|
public static String replace(String self, String tr0, String tr1, String... tr) {
|
||||||
|
// the first two parameters are required, the rest of the parameter sequence is optional
|
||||||
|
self = self.replace(tr0, tr1);
|
||||||
|
|
||||||
|
for (int i = 0; i < tr.length - 1; i += 2) {
|
||||||
|
String t = tr[i];
|
||||||
|
String r = tr[i + 1];
|
||||||
|
self = self.replace(t, r);
|
||||||
|
}
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* File utilities
|
||||||
|
*/
|
||||||
|
public static File getRoot(File self) {
|
||||||
|
return FileUtilities.listPath(self).get(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<File> getPathList(File self) {
|
||||||
|
return FileUtilities.listPath(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static File getRelativePathTail(File self, int tailSize) {
|
||||||
|
return FileUtilities.getRelativePathTail(self, tailSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long getDiskSpace(File self) {
|
||||||
|
List<File> list = FileUtilities.listPath(self);
|
||||||
|
for (int i = list.size() - 1; i >= 0; i--) {
|
||||||
|
if (list.get(i).exists()) {
|
||||||
|
long usableSpace = list.get(i).getUsableSpace();
|
||||||
|
if (usableSpace > 0) {
|
||||||
|
return usableSpace;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue