1 /* gnu/regexp/RETokenRepeated.java
2 Copyright (C) 2006 Free Software Foundation, Inc.
4 This file is part of GNU Classpath.
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING. If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library. Thus, the terms and
23 conditions of the GNU General Public License cover the whole
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module. An independent module is a module which is not derived from
33 or based on this library. If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so. If you do not wish to do so, delete this
36 exception statement from your version. */
39 package gnu.java.util.regex;
41 // import java.util.Vector;
42 // import java.util.Stack;
44 final class RETokenRepeated extends REToken {
45 private REToken token;
47 private boolean stingy;
48 private boolean possessive;
49 private int tokenFixedLength;
51 RETokenRepeated(int subIndex, REToken token, int min, int max) {
56 if (token.returnsFixedLengthMatches()) {
57 tokenFixedLength = token.getMaximumLength();
60 tokenFixedLength = -1;
64 /** Sets the minimal matching mode to true. */
69 /** Queries if this token has minimal matching enabled. */
74 /** Sets possessive matching mode to true. */
75 void makePossessive() {
79 /** Queries if this token has possessive matching enabled. */
80 boolean isPossessive() {
85 * The minimum length of a repeated token is the minimum length
86 * of the token multiplied by the minimum number of times it must
89 int getMinimumLength() {
90 return (min * token.getMinimumLength());
93 int getMaximumLength() {
94 if (max == Integer.MAX_VALUE) return Integer.MAX_VALUE;
95 int tmax = token.getMaximumLength();
96 if (tmax == Integer.MAX_VALUE) return tmax;
100 // The comment "MUST make a clone" below means that some tests
101 // failed without doing clone(),
103 private static class DoablesFinder {
105 private CharIndexed input;
106 private REMatch rematch;
107 private boolean findFirst;
109 private DoablesFinder(REToken tk, CharIndexed input, REMatch mymatch) {
112 this.rematch = (REMatch) mymatch.clone(); // MUST make a clone
113 this.rematch.backtrackStack = new BacktrackStack();
117 private REMatch find() {
118 int origin = rematch.index;
121 rem = tk.findMatch(input, rematch);
126 if (rematch.backtrackStack.empty()) {
130 BacktrackStack.Backtrack bt = rematch.backtrackStack.pop();
131 rem = bt.token.backtrack(bt.input, bt.match, bt.param);
132 if (rem != null) break;
135 if (rem == null) return null;
136 if (rem.index == origin) rem.empty = true;
138 return (REMatch) rem.clone(); // MUST make a clone.
142 return rematch.backtrackStack.empty();
146 REMatch findMatch(CharIndexed input, REMatch mymatch) {
147 if (tokenFixedLength >= 0) return findMatchFixedLength(input, mymatch);
148 BacktrackStack stack = new BacktrackStack();
149 stack.push(new StackedInfo(input, 0, mymatch, null, null));
150 return findMatch(stack);
153 REMatch backtrack(CharIndexed input, REMatch mymatch, Object param) {
154 if (tokenFixedLength >= 0) return backtrackFixedLength(input, mymatch, param);
155 return findMatch((BacktrackStack)param);
158 private static class StackedInfo extends BacktrackStack.Backtrack {
161 DoablesFinder finder;
162 StackedInfo(CharIndexed input, int numRepeats, REMatch match,
163 int[] visited, DoablesFinder finder) {
164 super(null, input, match, null);
165 this.numRepeats = numRepeats;
166 this.visited = visited;
167 this.finder = finder;
171 private REMatch findMatch(BacktrackStack stack) {
172 // Avoid using recursive calls.
176 if (stack.empty()) return null;
177 StackedInfo si = (StackedInfo)(stack.peek());
178 CharIndexed input = si.input;
179 int numRepeats = si.numRepeats;
180 REMatch mymatch = si.match;
181 int[] visited = si.visited;
182 DoablesFinder finder = si.finder;
184 if (mymatch.backtrackStack == null)
185 mymatch.backtrackStack = new BacktrackStack();
187 if (numRepeats >= max) {
189 REMatch m1 = matchRest(input, mymatch);
191 if (! stack.empty()) {
192 m1.backtrackStack.push(new BacktrackStack.Backtrack(
193 this, input, mymatch, stack));
203 if (finder == null) {
204 finder = new DoablesFinder(token, input, mymatch);
208 if (numRepeats < min) {
210 REMatch doable = finder.find();
211 if (doable == null) {
212 if (stack.empty()) return null;
216 if (finder.noMore()) stack.pop();
217 int newNumRepeats = (doable.empty ? min : numRepeats + 1);
218 stack.push(new StackedInfo(
219 input, newNumRepeats, doable, visited, null));
224 if (visited == null) visited = initVisited();
227 REMatch nextMatch = finder.find();
228 if (nextMatch != null && !nextMatch.empty) {
229 stack.push(new StackedInfo(
230 input, numRepeats + 1, nextMatch, visited, null));
235 REMatch m1 = matchRest(input, mymatch);
237 if (!stack.empty()) {
238 m1.backtrackStack.push(new BacktrackStack.Backtrack(
239 this, input, mymatch, stack));
248 visited = addVisited(mymatch.index, visited);
253 boolean emptyMatchFound = false;
258 REMatch doable = finder.find();
259 if (doable == null) {
262 if (doable.empty) emptyMatchFound = true;
264 if (!emptyMatchFound) {
265 int n = doable.index;
266 if (! visitedContains(n, visited)) {
267 visited = addVisited(n, visited);
270 continue DO_ONE_DOABLE;
272 stack.push(new StackedInfo(
273 input, numRepeats + 1, doable, visited, null));
274 REMatch m1 = findMatch(stack);
279 m1.backtrackStack.push(new BacktrackStack.Backtrack(
280 this, input, mymatch, stack));
285 REMatch m1 = matchRest(input, doable);
290 if (! stack.empty()) {
291 m1.backtrackStack.push(new BacktrackStack.Backtrack(
292 this, input, mymatch, stack));
300 } while (false); // DO_THIS only once;
302 if (!stack.empty()) {
308 REMatch m1 = matchRest(input, mymatch);
310 if (! stack.empty()) {
311 m1.backtrackStack.push(new BacktrackStack.Backtrack(
312 this, input, mymatch, stack));
320 boolean match(CharIndexed input, REMatch mymatch) {
321 REMatch m1 = findMatch(input, mymatch);
323 mymatch.assignFrom(m1);
329 // Array visited is an array of character positions we have already
330 // visited. visited[0] is used to store the effective length of the
332 private static int[] initVisited() {
333 int[] visited = new int[32];
338 private static boolean visitedContains(int n, int[] visited) {
339 // Experience tells that for a small array like this,
340 // simple linear search is faster than binary search.
341 for (int i = 1; i < visited[0]; i++) {
342 if (n == visited[i]) return true;
347 private static int[] addVisited(int n, int[] visited) {
348 if (visitedContains(n, visited)) return visited;
349 if (visited[0] >= visited.length - 1) {
350 int[] newvisited = new int[visited.length + 32];
351 System.arraycopy(visited, 0, newvisited, 0, visited.length);
352 visited = newvisited;
355 visited[visited[0]] = n;
359 private REMatch matchRest(CharIndexed input, final REMatch newMatch) {
360 if (next(input, newMatch)) {
366 private REMatch findMatchFixedLength(CharIndexed input, REMatch mymatch) {
367 if (mymatch.backtrackStack == null)
368 mymatch.backtrackStack = new BacktrackStack();
369 int numRepeats = token.findFixedLengthMatches(input, (REMatch)mymatch.clone(), max);
370 if (numRepeats == Integer.MAX_VALUE) numRepeats = min;
371 int count = numRepeats - min + 1;
372 if (count <= 0) return null;
374 if (!stingy) index = mymatch.index + (tokenFixedLength * numRepeats);
375 else index = mymatch.index + (tokenFixedLength * min);
376 return findMatchFixedLength(input, mymatch, index, count);
379 private REMatch backtrackFixedLength(CharIndexed input, REMatch mymatch,
381 int[] params = (int[])param;
382 int index = params[0];
383 int count = params[1];
384 return findMatchFixedLength(input, mymatch, index, count);
387 private REMatch findMatchFixedLength(CharIndexed input, REMatch mymatch,
388 int index, int count) {
389 REMatch tryMatch = (REMatch) mymatch.clone();
391 tryMatch.index = index;
392 REMatch m = matchRest(input, tryMatch);
394 if (stingy) index += tokenFixedLength;
395 else index -= tokenFixedLength;
396 if (possessive) return m;
399 m.backtrackStack.push(new BacktrackStack.Backtrack(
400 this, input, mymatch,
401 new int[] {index, count}));
405 if (count <= 0) return null;
409 void dump(StringBuffer os) {
413 if ((max == Integer.MAX_VALUE) && (min <= 1))
414 os.append( (min == 0) ? '*' : '+' );
415 else if ((min == 0) && (max == 1))
418 os.append('{').append(min);
421 if (max != Integer.MAX_VALUE) os.append(max);
425 if (stingy) os.append('?');