package php.runtime.ext.core.classes.util;
import php.runtime.Memory;
import php.runtime.annotation.Reflection;
import php.runtime.common.HintType;
import php.runtime.env.Environment;
import php.runtime.ext.java.JavaException;
import php.runtime.invoke.Invoker;
import php.runtime.lang.BaseObject;
import php.runtime.lang.ForeachIterator;
import php.runtime.lang.spl.iterator.Iterator;
import php.runtime.memory.*;
import php.runtime.reflection.ClassEntity;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static php.runtime.annotation.Reflection.*;
import static php.runtime.annotation.Runtime.FastMethod;
@Name("php\\util\\Regex")
final public class WrapRegex extends BaseObject implements Iterator {
public static final int CANON_EQ = Pattern.CANON_EQ;
public static final int CASE_INSENSITIVE = Pattern.CASE_INSENSITIVE;
public static final int UNICODE_CASE = Pattern.UNICODE_CASE;
public static final int UNICODE_CHARACTER_CLASS = Pattern.UNICODE_CHARACTER_CLASS;
public static final int COMMENTS = Pattern.COMMENTS;
public static final int DOTALL = Pattern.DOTALL;
public static final int LITERAL = Pattern.LITERAL;
public static final int MULTILINE = Pattern.MULTILINE;
public static final int UNIX_LINES = Pattern.UNIX_LINES;
protected Matcher matcher;
protected String input;
protected Memory current;
protected Memory key;
protected boolean valid = true;
public WrapRegex(Environment env, Matcher matcher, String input) {
super(env);
this.matcher = matcher;
this.input = input;
}
public WrapRegex(Environment env, ClassEntity clazz) {
super(env, clazz);
}
@Signature({
@Arg("pattern"),
@Arg(value = "flags", optional = @Reflection.Optional("0")),
@Arg(value = "string", optional = @Reflection.Optional(""))
})
public Memory __construct(Environment env, Memory... args) {
int flags = convertFlags(args[1]);
Pattern pattern = Pattern.compile(args[0].toString(), flags);
matcher = pattern.matcher(input = args[2].toString());
return Memory.NULL;
}
public Matcher getMatcher() {
return matcher;
}
public String getInput() {
return input;
}
@Signature
public Memory __debugInfo(Environment env, Memory... args) {
ArrayMemory r = new ArrayMemory();
r.refOfIndex("*pattern").assign(matcher.pattern().toString());
r.refOfIndex("*flags").assign(matcher.pattern().flags());
r.refOfIndex("*input").assign(input);
return r.toConstant();
}
@Signature(@Arg("string"))
public Memory with(Environment env, Memory... args) {
Matcher matcher1 = matcher.pattern().matcher(args[0].toString());
return new ObjectMemory(new WrapRegex(env, matcher1, args[0].toString()));
}
@Signature(@Arg(value = "start", optional = @Reflection.Optional("null")))
public Memory find(Environment env, Memory... args) {
if (args[0].isNull())
return matcher.find() ? Memory.TRUE : Memory.FALSE;
else
return matcher.find(args[0].toInteger()) ? Memory.TRUE : Memory.FALSE;
}
@Signature
public Memory matches(Environment env, Memory... args) {
return matcher.matches() ? Memory.TRUE : Memory.FALSE;
}
@FastMethod
@Signature(@Arg("string"))
public Memory test(Environment env, Memory... args) {
Matcher matcher1 = matcher.pattern().matcher(args[0].toString());
return TrueMemory.valueOf(matcher1.matches());
}
@Signature(@Arg(value = "string", optional = @Reflection.Optional("null")))
public Memory reset(Environment env, Memory... args) {
if (args[0].isNull())
matcher.reset();
else
matcher.reset(args[0].toString());
return new ObjectMemory(this);
}
@Signature(@Arg(value = "group", optional = @Reflection.Optional("null")))
public Memory end(Environment env, Memory... args) {
if (args[0].isNull())
return LongMemory.valueOf(matcher.end());
else
return LongMemory.valueOf(matcher.end(args[0].toInteger()));
}
@Signature(@Arg(value = "group", optional = @Reflection.Optional("null")))
public Memory start(Environment env, Memory... args) {
if (args[0].isNull())
return LongMemory.valueOf(matcher.start());
else
return LongMemory.valueOf(matcher.start(args[0].toInteger()));
}
@FastMethod
@Signature
public Memory getGroupCount(Environment env, Memory... args) {
return LongMemory.valueOf(matcher.groupCount());
}
@FastMethod
@Signature
public Memory hitEnd(Environment env, Memory... args) {
return matcher.hitEnd() ? Memory.TRUE : Memory.FALSE;
}
@FastMethod
@Signature
public Memory requireEnd(Environment env, Memory... args) {
return matcher.requireEnd() ? Memory.TRUE : Memory.FALSE;
}
@Signature
public Memory lookingAt(Environment env, Memory... args) {
return matcher.lookingAt() ? Memory.TRUE : Memory.FALSE;
}
@Signature({
@Arg("start"),
@Arg("end")
})
public Memory region(Environment env, Memory... args) {
matcher.region(args[0].toInteger(), args[1].toInteger());
return new ObjectMemory(this);
}
@FastMethod
@Signature
public Memory regionStart(Environment env, Memory... args) {
return LongMemory.valueOf(matcher.regionStart());
}
@FastMethod
@Signature
public Memory regionEnd(Environment env, Memory... args) {
return LongMemory.valueOf(matcher.regionEnd());
}
@Signature(@Arg(value = "start", optional = @Reflection.Optional("null")))
public Memory all(Environment env, Memory... args) {
int start = args[0].toInteger();
int count = 0;
ArrayMemory r = new ArrayMemory();
while ((count == 0 ? matcher.find(start) : matcher.find())) {
count++;
r.add(groups(env));
}
return r.toConstant();
}
@Signature(@Arg(value = "start", optional = @Reflection.Optional("null")))
public Memory last(Environment env, Memory... args) {
int count = 0, index = args[0].toInteger();
while ((count == 0 ? matcher.find(index) : matcher.find())) {
index = matcher.start();
count++;
}
if (count > 0) {
matcher.find(index);
return groups(env);
} else {
return Memory.NULL;
}
}
@Signature(@Arg(value = "start", optional = @Reflection.Optional("null")))
public Memory first(Environment env, Memory... args) {
return one(env, args);
}
@Signature(@Arg(value = "start", optional = @Reflection.Optional("null")))
public Memory one(Environment env, Memory... args) {
int start = args[0].toInteger();
if (matcher.find(start)) {
return groups(env);
}
return Memory.NULL;
}
@Signature
public Memory groups(Environment env, Memory... args) {
int count = matcher.groupCount();
ArrayMemory r = new ArrayMemory();
r.add(matcher.group());
for (int i = 0; i < count; i++) {
r.add(matcher.group(i + 1));
}
return r.toConstant();
}
@Signature(@Arg(value = "group", optional = @Reflection.Optional("null")))
public Memory group(Environment env, Memory... args) {
Memory group = args[0];
if (group.isNull())
return StringMemory.valueOf(matcher.group());
else {
if (group.isString() || group.isObject()) {
Memory longMemory = StringMemory.toLong(group.toString(), false);
if (longMemory == null) {
return StringMemory.valueOf(matcher.group(group.toString()));
}
}
return StringMemory.valueOf(matcher.group(group.toInteger()));
}
}
@Signature
public Memory groupNames() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
Map<String, Integer> namedGroups = getNamedGroups(matcher.pattern());
ArrayMemory r = new ArrayMemory();
for (String s : namedGroups.keySet()) {
r.put(s, StringMemory.valueOf(s));
}
return r.toConstant();
}
@SuppressWarnings("unchecked")
private static Map<String, Integer> getNamedGroups(Pattern regex)
throws NoSuchMethodException, SecurityException,
IllegalAccessException, IllegalArgumentException,
InvocationTargetException {
Method namedGroupsMethod = Pattern.class.getDeclaredMethod("namedGroups");
namedGroupsMethod.setAccessible(true);
Map<String, Integer> namedGroups = null;
return (Map<String, Integer>) namedGroupsMethod.invoke(regex);
}
@Signature(@Arg("replacement"))
public Memory replace(Environment env, Memory... args) {
try {
return StringMemory.valueOf(matcher.replaceAll(args[0].toString()));
} catch (IllegalStateException | IndexOutOfBoundsException e) {
throw new RegexException(env, e);
}
}
@Signature(@Arg("replacement"))
public Memory replaceFirst(Environment env, Memory... args) {
return StringMemory.valueOf(matcher.replaceFirst(args[0].toString()));
}
@Signature({@Arg("group"), @Arg("replacement")})
public Memory replaceGroup(Environment env, Memory... args) {
int group = args[0].toInteger();
matcher.reset();
if (matcher.find()) {
String what = matcher.group(group);
return StringMemory.valueOf(input.replace(what, args[1].toString()));
}
return StringMemory.valueOf(input);
}
@Signature(@Arg(value = "callback", type = HintType.CALLABLE))
public Memory replaceWithCallback(Environment env, Memory... args) {
Invoker invoker = Invoker.valueOf(env, null, args[0]);
StringBuffer sb = new StringBuffer();
ObjectMemory self = new ObjectMemory(this);
while (matcher.find()) {
Memory r = invoker.callNoThrow(self);
if (r.toValue() == Memory.FALSE)
break;
matcher.appendReplacement(sb, r.toString());
}
matcher.appendTail(sb);
return StringMemory.valueOf(sb.toString());
}
@Signature
public Memory getPattern(Environment env, Memory... args) {
return StringMemory.valueOf(matcher.pattern().pattern());
}
@Signature
public Memory getInput(Environment env, Memory... args) {
return StringMemory.valueOf(getInput());
}
@Signature
public Memory getFlags(Environment env, Memory... args) {
return LongMemory.valueOf(matcher.pattern().flags());
}
@Signature({
@Arg("pattern"),
@Arg(value = "flags", optional = @Reflection.Optional("0")),
@Arg(value = "string", optional = @Reflection.Optional(""))
})
public static Memory of(Environment env, Memory... args) {
int flags = convertFlags(args[1]);
Pattern pattern = Pattern.compile(args[0].toString(), flags);
Matcher matcher = pattern.matcher(args[2].toString());
return ObjectMemory.valueOf(new WrapRegex(env, matcher, ""));
}
@Signature(@Arg(value = "flags"))
public Memory withFlags(Environment env, Memory... args) {
int flags = convertFlags(args[0]);
Pattern pattern = Pattern.compile(matcher.pattern().pattern(), flags);
Matcher matcher1 = pattern.matcher(input);
return ObjectMemory.valueOf(new WrapRegex(env, matcher1, input));
}
@Override
@Signature
public Memory current(Environment env, Memory... args) {
return current == null ? Memory.NULL : current;
}
@Override
@Signature
public Memory key(Environment env, Memory... args) {
return key == null ? Memory.NULL : key;
}
@Override
@Signature
public Memory next(Environment env, Memory... args) {
valid = matcher.find();
if (valid) {
current = StringMemory.valueOf(matcher.group());
key = key.inc();
} else {
key = Memory.NULL;
current = Memory.NULL;
}
return Memory.NULL;
}
@Override
@Signature
public Memory rewind(Environment env, Memory... args) {
key = Memory.CONST_INT_M1;
matcher.reset();
next(env);
return Memory.NULL;
}
@Override
@Signature
public Memory valid(Environment env, Memory... args) {
return valid ? Memory.TRUE : Memory.FALSE;
}
@Signature({
@Arg("pattern"),
@Arg("string"),
@Arg(value = "flags", optional = @Optional("0"))
})
public static Memory match(Environment env, Memory... args) {
Pattern pattern = Pattern.compile(args[0].toString(), convertFlags(args[2]));
return pattern.matcher(args[1].toString()).find() ? Memory.TRUE : Memory.FALSE;
}
@Signature({
@Arg("pattern"),
@Arg("string"),
@Arg(value = "limit", optional = @Reflection.Optional("0"))
})
public static Memory split(Environment env, Memory... args) {
String[] r;
int limit = args[2].toInteger();
if (limit <= 0)
r = args[1].toString().split(args[0].toString());
else
r = args[1].toString().split(args[0].toString(), limit);
return ArrayMemory.ofStrings(r).toConstant();
}
@FastMethod
@Signature(@Arg("string"))
public static Memory quote(Environment env, Memory... args) {
return StringMemory.valueOf(Pattern.quote(args[0].toString()));
}
@FastMethod
@Signature(@Arg("string"))
public static Memory quoteReplacement(Environment env, Memory... args) {
return StringMemory.valueOf(Matcher.quoteReplacement(args[0].toString()));
}
@Signature
private Memory __clone(Environment env, Memory... args) {
return Memory.NULL;
}
@Override
public ForeachIterator getNewIterator(Environment env, boolean getReferences, boolean getKeyReferences) {
return ObjectMemory.valueOf(this).getNewIterator(env, getReferences, getKeyReferences);
}
@Override
public ForeachIterator getNewIterator(Environment env) {
return ObjectMemory.valueOf(this).getNewIterator(env);
}
private static int convertFlags(Memory _flags) {
int result = 0;
if (_flags.isNumber()) {
return _flags.toInteger();
}
String flags = _flags.toString();
if (StringMemory.toLong(flags) != null) {
return _flags.toInteger();
}
for (int i = 0; i < flags.length(); i++) {
char c = flags.charAt(i);
switch (c) {
case 'i':
result |= CASE_INSENSITIVE;
break;
case 'm':
result |= MULTILINE;
break;
case 'L':
result |= LITERAL;
break;
case 'd':
result |= UNIX_LINES;
break;
case 'u':
result |= UNICODE_CASE;
break;
case 'U':
result |= Pattern.UNICODE_CHARACTER_CLASS;
break;
case 'x':
result |= COMMENTS;
break;
case 's':
result |= DOTALL;
break;
}
}
return result;
}
@Name("php\\util\\RegexException")
public static class RegexException extends JavaException {
public RegexException(Environment env, Throwable throwable) {
super(env, throwable);
}
public RegexException(Environment env, ClassEntity clazz) {
super(env, clazz);
}
}
}