libjava/ChangeLog:
[official-gcc.git] / libjava / classpath / gnu / java / util / regex / RETokenRepeated.java
blob0ba880d3987f61c533ba44f5e7a5c3f00c0d3055
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)
9 any later version.
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
19 02110-1301 USA.
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
24 combination.
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 gnu.java.lang.CPStringBuilder;
43 import java.util.ArrayDeque;
44 import java.util.Deque;
46 final class RETokenRepeated extends REToken
48 private REToken token;
49 private int min, max;
50 private boolean stingy;
51 private boolean possessive;
52 private int tokenFixedLength;
54 RETokenRepeated (int subIndex, REToken token, int min, int max)
56 super (subIndex);
57 this.token = token;
58 this.min = min;
59 this.max = max;
60 if (token.returnsFixedLengthMatches ())
62 tokenFixedLength = token.getMaximumLength ();
64 else
66 tokenFixedLength = -1;
70 /** Sets the minimal matching mode to true. */
71 void makeStingy ()
73 stingy = true;
76 /** Queries if this token has minimal matching enabled. */
77 boolean isStingy ()
79 return stingy;
82 /** Sets possessive matching mode to true. */
83 void makePossessive ()
85 possessive = true;
88 /** Queries if this token has possessive matching enabled. */
89 boolean isPossessive ()
91 return possessive;
94 /**
95 * The minimum length of a repeated token is the minimum length
96 * of the token multiplied by the minimum number of times it must
97 * match.
99 int getMinimumLength ()
101 return (min * token.getMinimumLength ());
104 int getMaximumLength ()
106 if (max == Integer.MAX_VALUE)
107 return Integer.MAX_VALUE;
108 int tmax = token.getMaximumLength ();
109 if (tmax == Integer.MAX_VALUE)
110 return tmax;
111 return (max * tmax);
114 // The comment "MUST make a clone" below means that some tests
115 // failed without doing clone(),
117 private static class DoablesFinder
119 private REToken tk;
120 private CharIndexed input;
121 private REMatch rematch;
122 private boolean findFirst;
124 private DoablesFinder (REToken tk, CharIndexed input, REMatch mymatch)
126 this.tk = tk;
127 this.input = input;
128 this.rematch = (REMatch) mymatch.clone (); // MUST make a clone
129 this.rematch.backtrackStack = new BacktrackStack ();
130 findFirst = true;
133 private REMatch find ()
135 int origin = rematch.index;
136 REMatch rem;
137 if (findFirst)
139 rem = tk.findMatch (input, rematch);
140 findFirst = false;
142 else
144 while (true)
146 if (rematch.backtrackStack.empty ())
148 rem = null;
149 break;
151 BacktrackStack.Backtrack bt = rematch.backtrackStack.pop ();
152 rem = bt.token.backtrack (bt.input, bt.match, bt.param);
153 if (rem != null)
154 break;
157 if (rem == null)
158 return null;
159 if (rem.index == origin)
160 rem.empty = true;
161 rematch = rem;
162 return (REMatch) rem.clone (); // MUST make a clone.
165 boolean noMore ()
167 return rematch.backtrackStack.empty ();
171 REMatch findMatch (CharIndexed input, REMatch mymatch)
173 if (tokenFixedLength >= 0)
174 return findMatchFixedLength (input, mymatch);
175 BacktrackStack stack = new BacktrackStack ();
176 stack.push (new StackedInfo (input, 0, mymatch, null, null));
177 return findMatch (stack);
180 REMatch backtrack (CharIndexed input, REMatch mymatch, Object param)
182 if (tokenFixedLength >= 0)
183 return backtrackFixedLength (input, mymatch, param);
184 return findMatch ((BacktrackStack) param);
187 private static class StackedInfo extends BacktrackStack.Backtrack
189 int numRepeats;
190 int[] visited;
191 DoablesFinder finder;
192 StackedInfo (CharIndexed input, int numRepeats, REMatch match,
193 int[]visited, DoablesFinder finder)
195 super (null, input, match, null);
196 this.numRepeats = numRepeats;
197 this.visited = visited;
198 this.finder = finder;
202 private static class FindMatchControl
204 DoablesFinder finder;
205 FindMatchControl (DoablesFinder finder)
207 this.finder = finder;
211 private REMatch findMatch (BacktrackStack stack)
213 return findMatch (stack, new ArrayDeque < FindMatchControl > ());
216 private REMatch findMatch (BacktrackStack stack,
217 Deque < FindMatchControl > controlStack)
219 REMatch result = null;
220 StackedInfo si = null;
221 CharIndexed input = null;
222 int numRepeats = 0;
223 REMatch mymatch = null;
224 int[] visited = null;
225 DoablesFinder finder = null;
227 // Avoid using recursive calls because a match can be very long.
229 // This is the first entry point of this method.
230 // If you want to call this method recursively and you need the
231 // result returned, save necessary information in a FindMatchControl
232 // object and push it to controlStack, then continue from this point.
233 // You can check the result after exiting MAIN_LOOP.
234 MAIN_LOOP0:
235 while (true)
238 // This is the second entry point of this method.
239 // If you want to call this method recursively but you do not need the
240 // result returned, just continue from this point.
241 MAIN_LOOP:
242 while (true)
245 if (stack.empty ())
246 break MAIN_LOOP;
247 si = (StackedInfo) (stack.peek ());
248 input = si.input;
249 numRepeats = si.numRepeats;
250 mymatch = si.match;
251 visited = si.visited;
252 finder = si.finder;
254 if (mymatch.backtrackStack == null)
255 mymatch.backtrackStack = new BacktrackStack ();
257 if (numRepeats >= max)
259 stack.pop ();
260 REMatch m1 = matchRest (input, mymatch);
261 if (m1 != null)
263 if (!stack.empty ())
265 m1.backtrackStack.push (new BacktrackStack.
266 Backtrack (this, input,
267 mymatch, stack));
269 result = m1;
270 break MAIN_LOOP;
272 if (stingy)
274 continue MAIN_LOOP;
276 break MAIN_LOOP;
279 if (finder == null)
281 finder = new DoablesFinder (token, input, mymatch);
282 si.finder = finder;
285 if (numRepeats < min)
287 while (true)
289 REMatch doable = finder.find ();
290 if (doable == null)
292 if (stack.empty ())
293 return null;
294 stack.pop ();
295 continue MAIN_LOOP;
297 if (finder.noMore ())
298 stack.pop ();
299 int newNumRepeats = (doable.empty ? min : numRepeats + 1);
300 stack.
301 push (new
302 StackedInfo (input, newNumRepeats, doable,
303 visited, null));
304 continue MAIN_LOOP;
308 if (visited == null)
309 visited = initVisited ();
311 if (stingy)
313 REMatch nextMatch = finder.find ();
314 if (nextMatch != null && !nextMatch.empty)
316 stack.
317 push (new
318 StackedInfo (input, numRepeats + 1, nextMatch,
319 visited, null));
321 else
323 stack.pop ();
325 REMatch m1 = matchRest (input, mymatch);
326 if (m1 != null)
328 if (!stack.empty ())
330 m1.backtrackStack.push (new BacktrackStack.
331 Backtrack (this, input,
332 mymatch, stack));
334 result = m1;
335 break MAIN_LOOP;
337 else
339 continue MAIN_LOOP;
343 visited = addVisited (mymatch.index, visited);
345 TryAnotherResult taresult =
346 tryAnother (stack, input, mymatch, numRepeats, finder, visited);
347 visited = taresult.visited;
348 switch (taresult.status)
350 case TryAnotherResult.TRY_FURTHER:
351 controlStack.push (new FindMatchControl (finder));
352 continue MAIN_LOOP0;
353 case TryAnotherResult.RESULT_FOUND:
354 result = taresult.result;
355 break MAIN_LOOP;
358 if (!stack.empty ())
360 stack.pop ();
362 if (possessive)
364 stack.clear ();
366 REMatch m1 = matchRest (input, mymatch);
367 if (m1 != null)
369 if (!stack.empty ())
371 m1.backtrackStack.push (new BacktrackStack.
372 Backtrack (this, input, mymatch,
373 stack));
375 result = m1;
376 break MAIN_LOOP;
379 } // MAIN_LOOP
381 if (controlStack.isEmpty ())
382 return result;
383 FindMatchControl control = controlStack.pop ();
384 if (possessive)
386 return result;
388 if (result != null)
390 result.backtrackStack.push (new BacktrackStack.
391 Backtrack (this, input, mymatch,
392 stack));
393 return result;
396 finder = control.finder;
398 TryAnotherResult taresult =
399 tryAnother (stack, input, mymatch, numRepeats, finder, visited);
400 visited = taresult.visited;
401 switch (taresult.status)
403 case TryAnotherResult.TRY_FURTHER:
404 controlStack.push (new FindMatchControl (finder));
405 continue MAIN_LOOP0;
406 case TryAnotherResult.RESULT_FOUND:
407 return taresult.result;
409 continue MAIN_LOOP0;
411 } // MAIN_LOOP0
414 private static class TryAnotherResult
416 REMatch result;
417 int status;
418 static final int RESULT_FOUND = 1;
419 static final int TRY_FURTHER = 2;
420 static final int NOTHING_FOUND = 3;
421 int[] visited;
424 private TryAnotherResult tryAnother (BacktrackStack stack,
425 CharIndexed input, REMatch mymatch,
426 int numRepeats, DoablesFinder finder,
427 int[]visited)
430 TryAnotherResult taresult = new TryAnotherResult ();
431 taresult.visited = visited;
433 DO_THIS:
436 boolean emptyMatchFound = false;
438 DO_ONE_DOABLE:
439 while (true)
442 REMatch doable = finder.find ();
443 if (doable == null)
445 break DO_THIS;
447 if (doable.empty)
448 emptyMatchFound = true;
450 if (!emptyMatchFound)
452 int n = doable.index;
453 if (visitedContains (n, visited))
455 continue DO_ONE_DOABLE;
457 visited = addVisited (n, visited);
458 stack.
459 push (new
460 StackedInfo (input, numRepeats + 1, doable, visited,
461 null));
462 taresult.visited = visited;
463 taresult.status = TryAnotherResult.TRY_FURTHER;
464 return taresult;
466 else
468 REMatch m1 = matchRest (input, doable);
469 if (possessive)
471 taresult.result = m1;
472 taresult.status = TryAnotherResult.RESULT_FOUND;
473 return taresult;
475 if (m1 != null)
477 if (!stack.empty ())
479 m1.backtrackStack.push (new BacktrackStack.
480 Backtrack (this, input, mymatch,
481 stack));
483 taresult.result = m1;
484 taresult.status = TryAnotherResult.RESULT_FOUND;
485 return taresult;
489 } // DO_ONE_DOABLE
491 } // DO_THIS
493 taresult.status = TryAnotherResult.NOTHING_FOUND;
494 return taresult;
498 boolean match (CharIndexed input, REMatch mymatch)
500 setHitEnd (input, mymatch);
501 REMatch m1 = findMatch (input, mymatch);
502 if (m1 != null)
504 mymatch.assignFrom (m1);
505 return true;
507 return false;
510 // Array visited is an array of character positions we have already
511 // visited. visited[0] is used to store the effective length of the
512 // array.
513 private static int[] initVisited ()
515 int[] visited = new int[32];
516 visited[0] = 0;
517 return visited;
520 private static boolean visitedContains (int n, int[]visited)
522 // Experience tells that for a small array like this,
523 // simple linear search is faster than binary search.
524 for (int i = 1; i < visited[0]; i++)
526 if (n == visited[i])
527 return true;
529 return false;
532 private static int[] addVisited (int n, int[]visited)
534 if (visitedContains (n, visited))
535 return visited;
536 if (visited[0] >= visited.length - 1)
538 int[] newvisited = new int[visited.length + 32];
539 System.arraycopy (visited, 0, newvisited, 0, visited.length);
540 visited = newvisited;
542 visited[0]++;
543 visited[visited[0]] = n;
544 return visited;
547 private REMatch matchRest (CharIndexed input, final REMatch newMatch)
549 if (next (input, newMatch))
551 return newMatch;
553 return null;
556 private REMatch findMatchFixedLength (CharIndexed input, REMatch mymatch)
558 if (mymatch.backtrackStack == null)
559 mymatch.backtrackStack = new BacktrackStack ();
560 int numRepeats =
561 token.findFixedLengthMatches (input, (REMatch) mymatch.clone (), max);
562 if (numRepeats == Integer.MAX_VALUE)
563 numRepeats = min;
564 int count = numRepeats - min + 1;
565 if (count <= 0)
566 return null;
567 int index = 0;
568 if (!stingy)
569 index = mymatch.index + (tokenFixedLength * numRepeats);
570 else
571 index = mymatch.index + (tokenFixedLength * min);
572 return findMatchFixedLength (input, mymatch, index, count);
575 private REMatch backtrackFixedLength (CharIndexed input, REMatch mymatch,
576 Object param)
578 int[] params = (int[]) param;
579 int index = params[0];
580 int count = params[1];
581 return findMatchFixedLength (input, mymatch, index, count);
584 private REMatch findMatchFixedLength (CharIndexed input, REMatch mymatch,
585 int index, int count)
587 REMatch tryMatch = (REMatch) mymatch.clone ();
588 while (true)
590 tryMatch.index = index;
591 REMatch m = matchRest (input, tryMatch);
592 count--;
593 if (stingy)
594 index += tokenFixedLength;
595 else
596 index -= tokenFixedLength;
597 if (possessive)
598 return m;
599 if (m != null)
601 if (count > 0)
603 m.backtrackStack.push (new BacktrackStack.
604 Backtrack (this, input, mymatch,
605 new int[]
607 index, count}));
609 return m;
611 if (count <= 0)
612 return null;
616 void dump (CPStringBuilder os)
618 os.append ("(?:");
619 token.dumpAll (os);
620 os.append (')');
621 if ((max == Integer.MAX_VALUE) && (min <= 1))
622 os.append ((min == 0) ? '*' : '+');
623 else if ((min == 0) && (max == 1))
624 os.append ('?');
625 else
627 os.append ('{').append (min);
628 if (max > min)
630 os.append (',');
631 if (max != Integer.MAX_VALUE)
632 os.append (max);
634 os.append ('}');
636 if (stingy)
637 os.append ('?');