mirror of git://gcc.gnu.org/git/gcc.git
				
				
				
			
		
			
				
	
	
		
			640 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			Java
		
	
	
	
			
		
		
	
	
			640 lines
		
	
	
		
			18 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.java.util.regex;
 | |
| 
 | |
| import gnu.java.lang.CPStringBuilder;
 | |
| 
 | |
| import java.util.ArrayDeque;
 | |
| import java.util.Deque;
 | |
| 
 | |
| 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 static class FindMatchControl
 | |
|   {
 | |
|     DoablesFinder finder;
 | |
|       FindMatchControl (DoablesFinder finder)
 | |
|     {
 | |
|       this.finder = finder;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   private REMatch findMatch (BacktrackStack stack)
 | |
|   {
 | |
|     return findMatch (stack, new ArrayDeque < FindMatchControl > ());
 | |
|   }
 | |
| 
 | |
|   private REMatch findMatch (BacktrackStack stack,
 | |
|                              Deque < FindMatchControl > controlStack)
 | |
|   {
 | |
|     REMatch result = null;
 | |
|     StackedInfo si = null;
 | |
|     CharIndexed input = null;
 | |
|     int numRepeats = 0;
 | |
|     REMatch mymatch = null;
 | |
|     int[] visited = null;
 | |
|     DoablesFinder finder = null;
 | |
| 
 | |
|     // Avoid using recursive calls because a match can be very long.
 | |
| 
 | |
|     // This is the first entry point of this method.
 | |
|     // If you want to call this method recursively and you need the
 | |
|     // result returned, save necessary information in a FindMatchControl
 | |
|     // object and push it to controlStack, then continue from this point.
 | |
|     // You can check the result after exiting MAIN_LOOP.
 | |
|   MAIN_LOOP0:
 | |
|     while (true)
 | |
|       {
 | |
| 
 | |
|         // This is the second entry point of this method.
 | |
|         // If you want to call this method recursively but you do not need the
 | |
|         // result returned, just continue from this point.
 | |
|       MAIN_LOOP:
 | |
|         while (true)
 | |
|           {
 | |
| 
 | |
|             if (stack.empty ())
 | |
|               break MAIN_LOOP;
 | |
|             si = (StackedInfo) (stack.peek ());
 | |
|             input = si.input;
 | |
|             numRepeats = si.numRepeats;
 | |
|             mymatch = si.match;
 | |
|             visited = si.visited;
 | |
|             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));
 | |
|                       }
 | |
|                     result = m1;
 | |
|                     break MAIN_LOOP;
 | |
|                   }
 | |
|                 if (stingy)
 | |
|                   {
 | |
|                     continue MAIN_LOOP;
 | |
|                   }
 | |
|                 break MAIN_LOOP;
 | |
|               }
 | |
| 
 | |
|             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));
 | |
|                       }
 | |
|                     result = m1;
 | |
|                     break MAIN_LOOP;
 | |
|                   }
 | |
|                 else
 | |
|                   {
 | |
|                     continue MAIN_LOOP;
 | |
|                   }
 | |
|               }
 | |
| 
 | |
|             visited = addVisited (mymatch.index, visited);
 | |
| 
 | |
|             TryAnotherResult taresult =
 | |
|               tryAnother (stack, input, mymatch, numRepeats, finder, visited);
 | |
|             visited = taresult.visited;
 | |
|             switch (taresult.status)
 | |
|               {
 | |
|               case TryAnotherResult.TRY_FURTHER:
 | |
|                 controlStack.push (new FindMatchControl (finder));
 | |
|                 continue MAIN_LOOP0;
 | |
|               case TryAnotherResult.RESULT_FOUND:
 | |
|                 result = taresult.result;
 | |
|                 break MAIN_LOOP;
 | |
|               }
 | |
| 
 | |
|             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));
 | |
|                   }
 | |
|                 result = m1;
 | |
|                 break MAIN_LOOP;
 | |
|               }
 | |
| 
 | |
|           }                     // MAIN_LOOP
 | |
| 
 | |
|         if (controlStack.isEmpty ())
 | |
|           return result;
 | |
|         FindMatchControl control = controlStack.pop ();
 | |
|         if (possessive)
 | |
|           {
 | |
|             return result;
 | |
|           }
 | |
|         if (result != null)
 | |
|           {
 | |
|             result.backtrackStack.push (new BacktrackStack.
 | |
|                                         Backtrack (this, input, mymatch,
 | |
|                                                    stack));
 | |
|             return result;
 | |
|           }
 | |
| 
 | |
|         finder = control.finder;
 | |
| 
 | |
|         TryAnotherResult taresult =
 | |
|           tryAnother (stack, input, mymatch, numRepeats, finder, visited);
 | |
|         visited = taresult.visited;
 | |
|         switch (taresult.status)
 | |
|           {
 | |
|           case TryAnotherResult.TRY_FURTHER:
 | |
|             controlStack.push (new FindMatchControl (finder));
 | |
|             continue MAIN_LOOP0;
 | |
|           case TryAnotherResult.RESULT_FOUND:
 | |
|             return taresult.result;
 | |
|           }
 | |
|         continue MAIN_LOOP0;
 | |
| 
 | |
|       }                         // MAIN_LOOP0
 | |
|   }
 | |
| 
 | |
|   private static class TryAnotherResult
 | |
|   {
 | |
|     REMatch result;
 | |
|     int status;
 | |
|     static final int RESULT_FOUND = 1;
 | |
|     static final int TRY_FURTHER = 2;
 | |
|     static final int NOTHING_FOUND = 3;
 | |
|     int[] visited;
 | |
|   }
 | |
| 
 | |
|   private TryAnotherResult tryAnother (BacktrackStack stack,
 | |
|                                        CharIndexed input, REMatch mymatch,
 | |
|                                        int numRepeats, DoablesFinder finder,
 | |
|                                        int[]visited)
 | |
|   {
 | |
| 
 | |
|     TryAnotherResult taresult = new TryAnotherResult ();
 | |
|     taresult.visited = visited;
 | |
| 
 | |
|   DO_THIS:
 | |
|     {
 | |
| 
 | |
|       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))
 | |
|                 {
 | |
|                   continue DO_ONE_DOABLE;
 | |
|                 }
 | |
|               visited = addVisited (n, visited);
 | |
|               stack.
 | |
|                 push (new
 | |
|                       StackedInfo (input, numRepeats + 1, doable, visited,
 | |
|                                    null));
 | |
|               taresult.visited = visited;
 | |
|               taresult.status = TryAnotherResult.TRY_FURTHER;
 | |
|               return taresult;
 | |
|             }
 | |
|           else
 | |
|             {
 | |
|               REMatch m1 = matchRest (input, doable);
 | |
|               if (possessive)
 | |
|                 {
 | |
|                   taresult.result = m1;
 | |
|                   taresult.status = TryAnotherResult.RESULT_FOUND;
 | |
|                   return taresult;
 | |
|                 }
 | |
|               if (m1 != null)
 | |
|                 {
 | |
|                   if (!stack.empty ())
 | |
|                     {
 | |
|                       m1.backtrackStack.push (new BacktrackStack.
 | |
|                                               Backtrack (this, input, mymatch,
 | |
|                                                          stack));
 | |
|                     }
 | |
|                   taresult.result = m1;
 | |
|                   taresult.status = TryAnotherResult.RESULT_FOUND;
 | |
|                   return taresult;
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|         }                       // DO_ONE_DOABLE
 | |
| 
 | |
|     }                           // DO_THIS
 | |
| 
 | |
|     taresult.status = TryAnotherResult.NOTHING_FOUND;
 | |
|     return taresult;
 | |
| 
 | |
|   }
 | |
| 
 | |
|   boolean match (CharIndexed input, REMatch mymatch)
 | |
|   {
 | |
|     setHitEnd (input, 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 (CPStringBuilder 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 ('?');
 | |
|   }
 | |
| }
 |