* @return -1 when not match; offset of the end of matched string when match.
*/
private int match(Context con, Op op, int offset, int dx, int opts) {
final ExpressionTarget target = con.target;
final Stack opStack = new Stack();
final IntStack dataStack = new IntStack();
final boolean isSetIgnoreCase = isSet(opts, IGNORE_CASE);
int retValue = -1;
boolean returned = false;
for (;;) {
if (op == null || offset > con.limit || offset < con.start) {
if (op == null) {
retValue = isSet(opts, XMLSCHEMA_MODE) && offset != con.limit ? -1 : offset;
}
else {
retValue = -1;
}
returned = true;
}
else {
retValue = -1;
// dx value is either 1 or -1
switch (op.type) {
case Op.CHAR:
{
final int o1 = (dx > 0) ? offset : offset -1;
if (o1 >= con.limit || o1 < 0 || !matchChar(op.getData(), target.charAt(o1), isSetIgnoreCase)) {
returned = true;
break;
}
offset += dx;
op = op.next;
}
break;
case Op.DOT:
{
int o1 = (dx > 0) ? offset : offset - 1;
if (o1 >= con.limit || o1 < 0) {
returned = true;
break;
}
if (isSet(opts, SINGLE_LINE)) {
if (REUtil.isHighSurrogate(target.charAt(o1)) && o1+dx >= 0 && o1+dx < con.limit) {
o1 += dx;
}
}
else {
int ch = target.charAt(o1);
if (REUtil.isHighSurrogate(ch) && o1+dx >= 0 && o1+dx < con.limit) {
o1 += dx;
ch = REUtil.composeFromSurrogates(ch, target.charAt(o1));
}
if (isEOLChar(ch)) {
returned = true;
break;
}
}
offset = (dx > 0) ? o1 + 1 : o1;
op = op.next;
}
break;
case Op.RANGE:
case Op.NRANGE:
{
int o1 = (dx > 0) ? offset : offset -1;
if (o1 >= con.limit || o1 < 0) {
returned = true;
break;
}
int ch = target.charAt(offset);
if (REUtil.isHighSurrogate(ch) && o1+dx < con.limit && o1+dx >=0) {
o1 += dx;
ch = REUtil.composeFromSurrogates(ch, target.charAt(o1));
}
final RangeToken tok = op.getToken();
if (!tok.match(ch)) {
returned = true;
break;
}
offset = (dx > 0) ? o1+1 : o1;
op = op.next;
}
break;
case Op.ANCHOR:
{
if (!matchAnchor(target, op, con, offset, opts)) {
returned = true;
break;
}
op = op.next;
}
break;
case Op.BACKREFERENCE:
{
int refno = op.getData();
if (refno <= 0 || refno >= this.nofparen) {
throw new RuntimeException("Internal Error: Reference number must be more than zero: "+refno);
}
if (con.match.getBeginning(refno) < 0 || con.match.getEnd(refno) < 0) {
returned = true;
break;
}
int o2 = con.match.getBeginning(refno);
int literallen = con.match.getEnd(refno)-o2;
if (dx > 0) {
if (!target.regionMatches(isSetIgnoreCase, offset, con.limit, o2, literallen)) {
returned = true;
break;
}
offset += literallen;
}
else {
if (!target.regionMatches(isSetIgnoreCase, offset-literallen, con.limit, o2, literallen)) {
returned = true;
break;
}
offset -= literallen;
}
op = op.next;
}
break;
case Op.STRING:
{
String literal = op.getString();
int literallen = literal.length();
if (dx > 0) {
if (!target.regionMatches(isSetIgnoreCase, offset, con.limit, literal, literallen)) {
returned = true;
break;
}
offset += literallen;
}
else {
if (!target.regionMatches(isSetIgnoreCase, offset-literallen, con.limit, literal, literallen)) {
returned = true;
break;
}
offset -= literallen;
}
op = op.next;
}
break;
case Op.CLOSURE:
{
// Saves current position to avoid zero-width repeats.
final int id = op.getData();
if (con.closureContexts[id].contains(offset)) {
returned = true;
break;
}
con.closureContexts[id].addOffset(offset);
}
// fall through
case Op.QUESTION:
{
opStack.push(op);
dataStack.push(offset);
op = op.getChild();
}
break;
case Op.NONGREEDYCLOSURE:
case Op.NONGREEDYQUESTION:
{
opStack.push(op);
dataStack.push(offset);
op = op.next;
}
break;
case Op.UNION:
if (op.size() == 0) {
returned = true;
}
else {
opStack.push(op);
dataStack.push(0);
dataStack.push(offset);
op = op.elementAt(0);
}
break;
case Op.CAPTURE:
{
final int refno = op.getData();
if (con.match != null) {
if (refno > 0) {
dataStack.push(con.match.getBeginning(refno));
con.match.setBeginning(refno, offset);
}
else {
final int index = -refno;
dataStack.push(con.match.getEnd(index));
con.match.setEnd(index, offset);
}
opStack.push(op);
dataStack.push(offset);
}
op = op.next;
}
break;
case Op.LOOKAHEAD:
case Op.NEGATIVELOOKAHEAD:
case Op.LOOKBEHIND:
case Op.NEGATIVELOOKBEHIND:
{
opStack.push(op);
dataStack.push(dx);
dataStack.push(offset);
dx = (op.type == Op.LOOKAHEAD || op.type == Op.NEGATIVELOOKAHEAD) ? 1 : -1;
op = op.getChild();
}
break;
case Op.INDEPENDENT:
{
opStack.push(op);
dataStack.push(offset);
op = op.getChild();
}
break;
case Op.MODIFIER:
{
int localopts = opts;
localopts |= op.getData();
localopts &= ~op.getData2();
opStack.push(op);
dataStack.push(opts);
dataStack.push(offset);
opts = localopts;
op = op.getChild();
}
break;
case Op.CONDITION:
{
Op.ConditionOp cop = (Op.ConditionOp)op;
if (cop.refNumber > 0) {
if (cop.refNumber >= this.nofparen) {
throw new RuntimeException("Internal Error: Reference number must be more than zero: "+cop.refNumber);
}
if (con.match.getBeginning(cop.refNumber) >= 0
&& con.match.getEnd(cop.refNumber) >= 0) {
op = cop.yes;
}
else if (cop.no != null) {
op = cop.no;
}
else {
op = cop.next;
}
}
else {
opStack.push(op);
dataStack.push(offset);
op = cop.condition;
}
}
break;
default:
throw new RuntimeException("Unknown operation type: " + op.type);
}
}
// handle recursive operations
while (returned) {
// exhausted all the operations
if (opStack.isEmpty()) {
return retValue;
}
op = (Op) opStack.pop();
offset = dataStack.pop();
switch (op.type) {
case Op.CLOSURE:
case Op.QUESTION:
if (retValue < 0) {
op = op.next;
returned = false;
}
break;
case Op.NONGREEDYCLOSURE:
case Op.NONGREEDYQUESTION:
if (retValue < 0) {
op = op.getChild();
returned = false;
}
break;
case Op.UNION:
{
int unionIndex = dataStack.pop();
if (DEBUG) {
System.err.println("UNION: "+unionIndex+", ret="+retValue);
}
if (retValue < 0) {
if (++unionIndex < op.size()) {
opStack.push(op);
dataStack.push(unionIndex);
dataStack.push(offset);
op = op.elementAt(unionIndex);
returned = false;
}
else {
retValue = -1;
}
}
}
break;
case Op.CAPTURE:
final int refno = op.getData();
final int saved = dataStack.pop();
if (retValue < 0) {
if (refno > 0) {
con.match.setBeginning(refno, saved);
}
else {
con.match.setEnd(-refno, saved);
}
}
break;
case Op.LOOKAHEAD:
case Op.LOOKBEHIND:
{
dx = dataStack.pop();
if (0 <= retValue) {
op = op.next;
returned = false;
}
retValue = -1;
}
break;
case Op.NEGATIVELOOKAHEAD:
case Op.NEGATIVELOOKBEHIND:
{
dx = dataStack.pop();
if (0 > retValue) {
op = op.next;
returned = false;
}
retValue = -1;
}
break;
case Op.MODIFIER:
opts = dataStack.pop();
// fall through
case Op.INDEPENDENT:
if (retValue >= 0) {
offset = retValue;