
Imported GNU Classpath 0.90 * scripts/makemake.tcl: LocaleData.java moved to gnu/java/locale. * sources.am: Regenerated. * gcj/javaprims.h: Regenerated. * Makefile.in: Regenerated. * gcj/Makefile.in: Regenerated. * include/Makefile.in: Regenerated. * testsuite/Makefile.in: Regenerated. * gnu/java/lang/VMInstrumentationImpl.java: New override. * gnu/java/net/local/LocalSocketImpl.java: Likewise. * gnu/classpath/jdwp/VMMethod.java: Likewise. * gnu/classpath/jdwp/VMVirtualMachine.java: Update to latest interface. * java/lang/Thread.java: Add UncaughtExceptionHandler. * java/lang/reflect/Method.java: Implements GenericDeclaration and isSynthetic(), * java/lang/reflect/Field.java: Likewise. * java/lang/reflect/Constructor.java * java/lang/Class.java: Implements Type, GenericDeclaration, getSimpleName() and getEnclosing*() methods. * java/lang/Class.h: Add new public methods. * java/lang/Math.java: Add signum(), ulp() and log10(). * java/lang/natMath.cc (log10): New function. * java/security/VMSecureRandom.java: New override. * java/util/logging/Logger.java: Updated to latest classpath version. * java/util/logging/LogManager.java: New override. From-SVN: r113887
427 lines
12 KiB
Java
427 lines
12 KiB
Java
/* gnu/regexp/RETokenRepeated.java
|
|
Copyright (C) 2006 Free Software Foundation, Inc.
|
|
|
|
This file is part of GNU Classpath.
|
|
|
|
GNU Classpath is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2, or (at your option)
|
|
any later version.
|
|
|
|
GNU Classpath is distributed in the hope that it will be useful, but
|
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with GNU Classpath; see the file COPYING. If not, write to the
|
|
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
|
02110-1301 USA.
|
|
|
|
Linking this library statically or dynamically with other modules is
|
|
making a combined work based on this library. Thus, the terms and
|
|
conditions of the GNU General Public License cover the whole
|
|
combination.
|
|
|
|
As a special exception, the copyright holders of this library give you
|
|
permission to link this library with independent modules to produce an
|
|
executable, regardless of the license terms of these independent
|
|
modules, and to copy and distribute the resulting executable under
|
|
terms of your choice, provided that you also meet, for each linked
|
|
independent module, the terms and conditions of the license of that
|
|
module. An independent module is a module which is not derived from
|
|
or based on this library. If you modify this library, you may extend
|
|
this exception to your version of the library, but you are not
|
|
obligated to do so. If you do not wish to do so, delete this
|
|
exception statement from your version. */
|
|
|
|
|
|
package gnu.regexp;
|
|
|
|
// import java.util.Vector;
|
|
// import java.util.Stack;
|
|
|
|
final class RETokenRepeated extends REToken {
|
|
private REToken token;
|
|
private int min,max;
|
|
private boolean stingy;
|
|
private boolean possessive;
|
|
private int tokenFixedLength;
|
|
|
|
RETokenRepeated(int subIndex, REToken token, int min, int max) {
|
|
super(subIndex);
|
|
this.token = token;
|
|
this.min = min;
|
|
this.max = max;
|
|
if (token.returnsFixedLengthMatches()) {
|
|
tokenFixedLength = token.getMaximumLength();
|
|
}
|
|
else {
|
|
tokenFixedLength = -1;
|
|
}
|
|
}
|
|
|
|
/** Sets the minimal matching mode to true. */
|
|
void makeStingy() {
|
|
stingy = true;
|
|
}
|
|
|
|
/** Queries if this token has minimal matching enabled. */
|
|
boolean isStingy() {
|
|
return stingy;
|
|
}
|
|
|
|
/** Sets possessive matching mode to true. */
|
|
void makePossessive() {
|
|
possessive = true;
|
|
}
|
|
|
|
/** Queries if this token has possessive matching enabled. */
|
|
boolean isPossessive() {
|
|
return possessive;
|
|
}
|
|
|
|
/**
|
|
* The minimum length of a repeated token is the minimum length
|
|
* of the token multiplied by the minimum number of times it must
|
|
* match.
|
|
*/
|
|
int getMinimumLength() {
|
|
return (min * token.getMinimumLength());
|
|
}
|
|
|
|
int getMaximumLength() {
|
|
if (max == Integer.MAX_VALUE) return Integer.MAX_VALUE;
|
|
int tmax = token.getMaximumLength();
|
|
if (tmax == Integer.MAX_VALUE) return tmax;
|
|
return (max * tmax);
|
|
}
|
|
|
|
// The comment "MUST make a clone" below means that some tests
|
|
// failed without doing clone(),
|
|
|
|
private static class DoablesFinder {
|
|
private REToken tk;
|
|
private CharIndexed input;
|
|
private REMatch rematch;
|
|
private boolean findFirst;
|
|
|
|
private DoablesFinder(REToken tk, CharIndexed input, REMatch mymatch) {
|
|
this.tk = tk;
|
|
this.input = input;
|
|
this.rematch = (REMatch) mymatch.clone(); // MUST make a clone
|
|
this.rematch.backtrackStack = new BacktrackStack();
|
|
findFirst = true;
|
|
}
|
|
|
|
private REMatch find() {
|
|
int origin = rematch.index;
|
|
REMatch rem;
|
|
if (findFirst) {
|
|
rem = tk.findMatch(input, rematch);
|
|
findFirst = false;
|
|
}
|
|
else {
|
|
while (true) {
|
|
if (rematch.backtrackStack.empty()) {
|
|
rem = null;
|
|
break;
|
|
}
|
|
BacktrackStack.Backtrack bt = rematch.backtrackStack.pop();
|
|
rem = bt.token.backtrack(bt.input, bt.match, bt.param);
|
|
if (rem != null) break;
|
|
}
|
|
}
|
|
if (rem == null) return null;
|
|
if (rem.index == origin) rem.empty = true;
|
|
rematch = rem;
|
|
return (REMatch) rem.clone(); // MUST make a clone.
|
|
}
|
|
|
|
boolean noMore() {
|
|
return rematch.backtrackStack.empty();
|
|
}
|
|
}
|
|
|
|
REMatch findMatch(CharIndexed input, REMatch mymatch) {
|
|
if (tokenFixedLength >= 0) return findMatchFixedLength(input, mymatch);
|
|
BacktrackStack stack = new BacktrackStack();
|
|
stack.push(new StackedInfo(input, 0, mymatch, null, null));
|
|
return findMatch(stack);
|
|
}
|
|
|
|
REMatch backtrack(CharIndexed input, REMatch mymatch, Object param) {
|
|
if (tokenFixedLength >= 0) return backtrackFixedLength(input, mymatch, param);
|
|
return findMatch((BacktrackStack)param);
|
|
}
|
|
|
|
private static class StackedInfo extends BacktrackStack.Backtrack {
|
|
int numRepeats;
|
|
int[] visited;
|
|
DoablesFinder finder;
|
|
StackedInfo(CharIndexed input, int numRepeats, REMatch match,
|
|
int[] visited, DoablesFinder finder) {
|
|
super(null, input, match, null);
|
|
this.numRepeats = numRepeats;
|
|
this.visited = visited;
|
|
this.finder = finder;
|
|
}
|
|
}
|
|
|
|
private REMatch findMatch(BacktrackStack stack) {
|
|
// Avoid using recursive calls.
|
|
MAIN_LOOP:
|
|
while (true) {
|
|
|
|
if (stack.empty()) return null;
|
|
StackedInfo si = (StackedInfo)(stack.peek());
|
|
CharIndexed input = si.input;
|
|
int numRepeats = si.numRepeats;
|
|
REMatch mymatch = si.match;
|
|
int[] visited = si.visited;
|
|
DoablesFinder finder = si.finder;
|
|
|
|
if (mymatch.backtrackStack == null)
|
|
mymatch.backtrackStack = new BacktrackStack();
|
|
|
|
if (numRepeats >= max) {
|
|
stack.pop();
|
|
REMatch m1 = matchRest(input, mymatch);
|
|
if (m1 != null) {
|
|
if (! stack.empty()) {
|
|
m1.backtrackStack.push(new BacktrackStack.Backtrack(
|
|
this, input, mymatch, stack));
|
|
}
|
|
return m1;
|
|
}
|
|
if (stingy) {
|
|
continue MAIN_LOOP;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
if (finder == null) {
|
|
finder = new DoablesFinder(token, input, mymatch);
|
|
si.finder = finder;
|
|
}
|
|
|
|
if (numRepeats < min) {
|
|
while (true) {
|
|
REMatch doable = finder.find();
|
|
if (doable == null) {
|
|
if (stack.empty()) return null;
|
|
stack.pop();
|
|
continue MAIN_LOOP;
|
|
}
|
|
if (finder.noMore()) stack.pop();
|
|
int newNumRepeats = (doable.empty ? min : numRepeats + 1);
|
|
stack.push(new StackedInfo(
|
|
input, newNumRepeats, doable, visited, null));
|
|
continue MAIN_LOOP;
|
|
}
|
|
}
|
|
|
|
if (visited == null) visited = initVisited();
|
|
|
|
if (stingy) {
|
|
REMatch nextMatch = finder.find();
|
|
if (nextMatch != null && !nextMatch.empty) {
|
|
stack.push(new StackedInfo(
|
|
input, numRepeats + 1, nextMatch, visited, null));
|
|
}
|
|
else {
|
|
stack.pop();
|
|
}
|
|
REMatch m1 = matchRest(input, mymatch);
|
|
if (m1 != null) {
|
|
if (!stack.empty()) {
|
|
m1.backtrackStack.push(new BacktrackStack.Backtrack(
|
|
this, input, mymatch, stack));
|
|
}
|
|
return m1;
|
|
}
|
|
else {
|
|
continue MAIN_LOOP;
|
|
}
|
|
}
|
|
|
|
visited = addVisited(mymatch.index, visited);
|
|
|
|
DO_THIS:
|
|
do {
|
|
|
|
boolean emptyMatchFound = false;
|
|
|
|
DO_ONE_DOABLE:
|
|
while (true) {
|
|
|
|
REMatch doable = finder.find();
|
|
if (doable == null) {
|
|
break DO_THIS;
|
|
}
|
|
if (doable.empty) emptyMatchFound = true;
|
|
|
|
if (!emptyMatchFound) {
|
|
int n = doable.index;
|
|
if (! visitedContains(n, visited)) {
|
|
visited = addVisited(n, visited);
|
|
}
|
|
else {
|
|
continue DO_ONE_DOABLE;
|
|
}
|
|
stack.push(new StackedInfo(
|
|
input, numRepeats + 1, doable, visited, null));
|
|
REMatch m1 = findMatch(stack);
|
|
if (possessive) {
|
|
return m1;
|
|
}
|
|
if (m1 != null) {
|
|
m1.backtrackStack.push(new BacktrackStack.Backtrack(
|
|
this, input, mymatch, stack));
|
|
return m1;
|
|
}
|
|
}
|
|
else {
|
|
REMatch m1 = matchRest(input, doable);
|
|
if (possessive) {
|
|
return m1;
|
|
}
|
|
if (m1 != null) {
|
|
if (! stack.empty()) {
|
|
m1.backtrackStack.push(new BacktrackStack.Backtrack(
|
|
this, input, mymatch, stack));
|
|
}
|
|
return m1;
|
|
}
|
|
}
|
|
|
|
} // DO_ONE_DOABLE
|
|
|
|
} while (false); // DO_THIS only once;
|
|
|
|
if (!stack.empty()) {
|
|
stack.pop();
|
|
}
|
|
if (possessive) {
|
|
stack.clear();
|
|
}
|
|
REMatch m1 = matchRest(input, mymatch);
|
|
if (m1 != null) {
|
|
if (! stack.empty()) {
|
|
m1.backtrackStack.push(new BacktrackStack.Backtrack(
|
|
this, input, mymatch, stack));
|
|
}
|
|
return m1;
|
|
}
|
|
|
|
} // MAIN_LOOP
|
|
}
|
|
|
|
boolean match(CharIndexed input, REMatch mymatch) {
|
|
REMatch m1 = findMatch(input, mymatch);
|
|
if (m1 != null) {
|
|
mymatch.assignFrom(m1);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Array visited is an array of character positions we have already
|
|
// visited. visited[0] is used to store the effective length of the
|
|
// array.
|
|
private static int[] initVisited() {
|
|
int[] visited = new int[32];
|
|
visited[0] = 0;
|
|
return visited;
|
|
}
|
|
|
|
private static boolean visitedContains(int n, int[] visited) {
|
|
// Experience tells that for a small array like this,
|
|
// simple linear search is faster than binary search.
|
|
for (int i = 1; i < visited[0]; i++) {
|
|
if (n == visited[i]) return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private static int[] addVisited(int n, int[] visited) {
|
|
if (visitedContains(n, visited)) return visited;
|
|
if (visited[0] >= visited.length - 1) {
|
|
int[] newvisited = new int[visited.length + 32];
|
|
System.arraycopy(visited, 0, newvisited, 0, visited.length);
|
|
visited = newvisited;
|
|
}
|
|
visited[0]++;
|
|
visited[visited[0]] = n;
|
|
return visited;
|
|
}
|
|
|
|
private REMatch matchRest(CharIndexed input, final REMatch newMatch) {
|
|
if (next(input, newMatch)) {
|
|
return newMatch;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
private REMatch findMatchFixedLength(CharIndexed input, REMatch mymatch) {
|
|
if (mymatch.backtrackStack == null)
|
|
mymatch.backtrackStack = new BacktrackStack();
|
|
int numRepeats = token.findFixedLengthMatches(input, (REMatch)mymatch.clone(), max);
|
|
if (numRepeats == Integer.MAX_VALUE) numRepeats = min;
|
|
int count = numRepeats - min + 1;
|
|
if (count <= 0) return null;
|
|
int index = 0;
|
|
if (!stingy) index = mymatch.index + (tokenFixedLength * numRepeats);
|
|
else index = mymatch.index + (tokenFixedLength * min);
|
|
return findMatchFixedLength(input, mymatch, index, count);
|
|
}
|
|
|
|
private REMatch backtrackFixedLength(CharIndexed input, REMatch mymatch,
|
|
Object param) {
|
|
int[] params = (int[])param;
|
|
int index = params[0];
|
|
int count = params[1];
|
|
return findMatchFixedLength(input, mymatch, index, count);
|
|
}
|
|
|
|
private REMatch findMatchFixedLength(CharIndexed input, REMatch mymatch,
|
|
int index, int count) {
|
|
REMatch tryMatch = (REMatch) mymatch.clone();
|
|
while (true) {
|
|
tryMatch.index = index;
|
|
REMatch m = matchRest(input, tryMatch);
|
|
count--;
|
|
if (stingy) index += tokenFixedLength;
|
|
else index -= tokenFixedLength;
|
|
if (possessive) return m;
|
|
if (m != null) {
|
|
if (count > 0) {
|
|
m.backtrackStack.push(new BacktrackStack.Backtrack(
|
|
this, input, mymatch,
|
|
new int[] {index, count}));
|
|
}
|
|
return m;
|
|
}
|
|
if (count <= 0) return null;
|
|
}
|
|
}
|
|
|
|
void dump(StringBuffer os) {
|
|
os.append("(?:");
|
|
token.dumpAll(os);
|
|
os.append(')');
|
|
if ((max == Integer.MAX_VALUE) && (min <= 1))
|
|
os.append( (min == 0) ? '*' : '+' );
|
|
else if ((min == 0) && (max == 1))
|
|
os.append('?');
|
|
else {
|
|
os.append('{').append(min);
|
|
if (max > min) {
|
|
os.append(',');
|
|
if (max != Integer.MAX_VALUE) os.append(max);
|
|
}
|
|
os.append('}');
|
|
}
|
|
if (stingy) os.append('?');
|
|
}
|
|
}
|