* @param context the dynamic evaluation context
* @param action defines the processing to be performed at the start and end of a group
*/
public void processMatchingSubstring(XPathContext context, OnGroup action) throws XPathException {
Receiver out = context.getReceiver();
int c = matcher.getParenCount()-1;
if (c == 0) {
out.characters(current.toString());
} else {
// Create a map from positions in the string to lists of actions.
// The "actions" in each list are: +N: start group N; -N: end group N.
IntHashMap<List<Integer>> actions = new IntHashMap<List<Integer>>(c);
for (int i=1; i<=c; i++) {
int start = matcher.getParenStart(i) - matcher.getParenStart(0);
if (start != -1) {
int end = matcher.getParenEnd(i) - matcher.getParenStart(0);
if (start < end) {
// Add the start action after all other actions on the list for the same position
List<Integer> s = actions.get(start);
if (s == null) {
s = new ArrayList<Integer>(4);
actions.put(start, s);
}
s.add(i);
// Add the end action before all other actions on the list for the same position
List<Integer> e = actions.get(end);
if (e == null) {
e = new ArrayList<Integer>(4);
actions.put(end, e);
}
e.add(0, -i);
} else {
// zero-length group (start==end). The problem here is that the information available
// from Java isn't sufficient to determine the nesting of groups: match("a", "(a(b?))")
// and match("a", "(a)(b?)") will both give the same result for group 2 (start=1, end=1).
// So we need to go back to the original regex to determine the group nesting
if (nestingTable == null) {
computeNestingTable();
}
int parentGroup = nestingTable.get(i);
// insert the start and end events immediately before the end event for the parent group,
// if present; otherwise after all existing events for this position
List<Integer> s = actions.get(start);
if (s == null) {
s = new ArrayList<Integer>(4);
actions.put(start, s);
s.add(i);
s.add(-i);
} else {
int pos = s.size();
for (int e=0; e<s.size(); e++) {
if (s.get(e) == -parentGroup) {
pos = e;
break;
}
}
s.add(pos, -i);
s.add(pos, i);
}
}
}
}
FastStringBuffer buff = new FastStringBuffer(current.length());
for (int i=0; i < current.length()+1; i++) {
List<Integer> events = actions.get(i);
if (events != null) {
if (buff.length() > 0) {
out.characters(buff);
buff.setLength(0);
}
for (Integer group : events) {
if (group > 0) {
action.onGroupStart(context, group);
} else {
action.onGroupEnd(context, -group);
}
}
}
if (i < current.length()) {
buff.appendWideChar(current.charAt(i));
}
}
if (buff.length() > 0) {
out.characters(buff);
}
}
}