1  /*
     2   * Copyright the original author or authors.
     3   * 
     4   * Licensed under the MOZILLA PUBLIC LICENSE, Version 1.1 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   * 
     8   *      http://www.mozilla.org/MPL/MPL-1.1.html
     9   * 
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16   
    17  import org.as2lib.regexp.Pattern;
    18  import org.as2lib.regexp.node.Node; 
    19  import org.as2lib.regexp.node.TreeInfo;
    20  
    21  /**
    22   * {@code Curly} class handles the curly-brace style repetition with a 
    23   * specified minimum and maximum occurrences. The * quantifier is handled 
    24   * as a special case. This class handles the three types.
    25   * 
    26   * @author Igor Sadovskiy
    27   */
    28   
    29  class org.as2lib.regexp.node.Curly extends Node {
    30  	
    31      private var atom:Node;
    32      private var type:Number;
    33      private var cmin:Number;
    34      private var cmax:Number;
    35  
    36      public function Curly(node:Node, cmin:Number, cmax:Number, type:Number) {
    37          this.atom = node;
    38          this.type = type;
    39          this.cmin = cmin;
    40          this.cmax = cmax;
    41      }
    42      
    43      public function match(matcher:Object, i:Number, seq:String):Boolean {
    44          var j:Number;
    45          for (j = 0; j < cmin; j++) {
    46              if (atom.match(matcher, i, seq)) {
    47                  i = matcher.last;
    48                  continue;
    49              }
    50              return false;
    51          }
    52          if (type == Pattern.GREEDY)
    53              return match0(matcher, i, j, seq);
    54          else if (type == Pattern.LAZY)
    55              return match1(matcher, i, j, seq);
    56          else
    57              return match2(matcher, i, j, seq);
    58      }
    59      
    60      // Greedy match.
    61      // i is the index to start matching at
    62      // j is the number of atoms that have matched
    63      private function match0(matcher:Object, i:Number, j:Number, seq:String):Boolean {
    64          if (j >= cmax) {
    65              // We have matched the maximum... continue with the rest of
    66              // the regular expression
    67              return next.match(matcher, i, seq);
    68          }
    69          var backLimit:Number = j;
    70          while (atom.match(matcher, i, seq)) {
    71              // k is the length of this match
    72              var k:Number = matcher.last - i;
    73              
    74              // Zero length match
    75              if (k == 0) break;
    76              
    77              // Move up index and number matched
    78              i = matcher.last;
    79              j++;
    80              
    81              // We are greedy so match as many as we can
    82              while (j < cmax) {
    83                  if (!atom.match(matcher, i, seq)) break;
    84                  if (i + k != matcher.last) {
    85                      if (match0(matcher, matcher.last, j+1, seq)) return true;
    86                      break;
    87                  }
    88                  i += k;
    89                  j++;
    90              }
    91              
    92              // Handle backing off if match fails
    93              while (j >= backLimit) {
    94                 if (next.match(matcher, i, seq))
    95                      return true;
    96                  i -= k;
    97                  j--;
    98              }
    99              return false;
   100          }
   101          return next.match(matcher, i, seq);
   102      }
   103      
   104      // Reluctant match. At this point, the minimum has been satisfied.
   105      // i is the index to start matching at
   106      // j is the number of atoms that have matched
   107      private function match1(matcher:Object, i:Number, j:Number, seq:String):Boolean {
   108          while (true) {
   109              // Try finishing match without consuming any more
   110              if (next.match(matcher, i, seq))
   111                  return true;
   112              // At the maximum, no match found
   113              if (j >= cmax)
   114                  return false;
   115              // Okay, must try one more atom
   116              if (!atom.match(matcher, i, seq))
   117                  return false;
   118              // If we haven't moved forward then must break out
   119              if (i == matcher.last)
   120                  return false;
   121              // Move up index and number matched
   122              i = matcher.last;
   123              j++;
   124          }
   125      }
   126      
   127      private function match2(matcher:Object, i:Number, j:Number, seq:String):Boolean {
   128          for (; j < cmax; j++) {
   129              if (!atom.match(matcher, i, seq)) 
   130              	break;
   131              if (i == matcher.last) 
   132              	break;
   133              i = matcher.last;
   134          }
   135          return next.match(matcher, i, seq);
   136      }
   137      
   138      public function study(info:TreeInfo):Boolean {
   139          // Save original info
   140          var minL:Number = info.minLength;
   141          var maxL:Number = info.maxLength;
   142          var maxV:Boolean = info.maxValid;
   143          var detm:Boolean = info.deterministic;
   144          info.reset();
   145  
   146          atom.study(info);
   147  
   148          var temp:Number = info.minLength * cmin + minL;
   149          if (temp < minL) {
   150              temp = 0xFFFFFFF; // arbitrary large number
   151          }
   152          info.minLength = temp;
   153  
   154          if (maxV && info.maxValid) {
   155              temp = info.maxLength * cmax + maxL;
   156              info.maxLength = temp;
   157              if (temp < maxL) {
   158                  info.maxValid = false;
   159              }
   160          } else {
   161              info.maxValid = false;
   162          }
   163  
   164          if (info.deterministic && cmin == cmax)
   165              info.deterministic = detm;
   166          else
   167              info.deterministic = false;
   168  
   169          return next.study(info);
   170      }
   171      
   172      public function getType(Void):Number {
   173      	return type;
   174      }
   175  
   176      public function getAtom(Void):Node {
   177      	return atom;
   178      }
   179      
   180      public function getCmin(Void):Number {
   181      	return cmin;
   182      }
   183      
   184      public function getCmax(Void):Number {
   185      	return cmax;
   186      }
   187      
   188  }
   189  
   190