Formatting
[jalview.git] / src / com / stevesoft / pat / Regex.java
1 //\r
2 // This software is now distributed according to\r
3 // the Lesser Gnu Public License.  Please see\r
4 // http://www.gnu.org/copyleft/lesser.txt for\r
5 // the details.\r
6 //    -- Happy Computing!\r
7 //\r
8 package com.stevesoft.pat;\r
9 \r
10 import java.io.*;\r
11 import java.util.*;\r
12 \r
13 import com.stevesoft.pat.wrap.*;\r
14 \r
15 /** Matches a Unicode punctuation character. */\r
16 class UnicodePunct\r
17     extends UniValidator\r
18 {\r
19   public int validate(StringLike s, int from, int to)\r
20   {\r
21     return from < s.length() && Prop.isPunct(s.charAt(from)) ? to : -1;\r
22   }\r
23 }\r
24 \r
25 /** Matches a Unicode white space character. */\r
26 class UnicodeWhite\r
27     extends UniValidator\r
28 {\r
29   public int validate(StringLike s, int from, int to)\r
30   {\r
31     return from < s.length() && Prop.isWhite(s.charAt(from)) ? to : -1;\r
32   }\r
33 }\r
34 \r
35 /** Matches a character that is not a Unicode punctuation\r
36  * character.\r
37  */\r
38 class NUnicodePunct\r
39     extends UniValidator\r
40 {\r
41   public int validate(StringLike s, int from, int to)\r
42   {\r
43     return from < s.length() && !Prop.isPunct(s.charAt(from)) ? to : -1;\r
44   }\r
45 }\r
46 \r
47 /** Matches a character that is not a\r
48  * Unicode white space character.\r
49  */\r
50 class NUnicodeWhite\r
51     extends UniValidator\r
52 {\r
53   public int validate(StringLike s, int from, int to)\r
54   {\r
55     return from < s.length() && !Prop.isWhite(s.charAt(from)) ? to : -1;\r
56   }\r
57 }\r
58 \r
59 /** Matches a Unicode word character: an alphanumeric or underscore. */\r
60 class UnicodeW\r
61     extends UniValidator\r
62 {\r
63   public int validate(StringLike s, int from, int to)\r
64   {\r
65     if (from >= s.length())\r
66     {\r
67       return -1;\r
68     }\r
69     char c = s.charAt(from);\r
70     return (Prop.isAlphabetic(c) || Prop.isDecimalDigit(c) || c == '_') ? to :\r
71         -1;\r
72   }\r
73 }\r
74 \r
75 /** Matches a character that is not a Unicode alphanumeric or underscore. */\r
76 class NUnicodeW\r
77     extends UniValidator\r
78 {\r
79   public int validate(StringLike s, int from, int to)\r
80   {\r
81     if (from >= s.length())\r
82     {\r
83       return -1;\r
84     }\r
85     char c = s.charAt(from);\r
86     return! (Prop.isAlphabetic(c) || Prop.isDecimalDigit(c) || c == '_') ? to :\r
87         -1;\r
88   }\r
89 }\r
90 \r
91 /** Matches a Unicode decimal digit. */\r
92 class UnicodeDigit\r
93     extends UniValidator\r
94 {\r
95   public int validate(StringLike s, int from, int to)\r
96   {\r
97     return from < s.length() && Prop.isDecimalDigit(s.charAt(from)) ? to : -1;\r
98   }\r
99 }\r
100 \r
101 /** Matches a character that is not a Unicode digit.*/\r
102 class NUnicodeDigit\r
103     extends UniValidator\r
104 {\r
105   public int validate(StringLike s, int from, int to)\r
106   {\r
107     return from < s.length() && !Prop.isDecimalDigit(s.charAt(from)) ? to : -1;\r
108   }\r
109 }\r
110 \r
111 /** Matches a Unicode math character. */\r
112 class UnicodeMath\r
113     extends UniValidator\r
114 {\r
115   public int validate(StringLike s, int from, int to)\r
116   {\r
117     return from < s.length() && Prop.isMath(s.charAt(from)) ? to : -1;\r
118   }\r
119 }\r
120 \r
121 /** Matches a non-math Unicode character. */\r
122 class NUnicodeMath\r
123     extends UniValidator\r
124 {\r
125   public int validate(StringLike s, int from, int to)\r
126   {\r
127     return from < s.length() && !Prop.isMath(s.charAt(from)) ? to : -1;\r
128   }\r
129 }\r
130 \r
131 /** Matches a Unicode currency symbol. */\r
132 class UnicodeCurrency\r
133     extends UniValidator\r
134 {\r
135   public int validate(StringLike s, int from, int to)\r
136   {\r
137     return from < s.length() && Prop.isCurrency(s.charAt(from)) ? to : -1;\r
138   }\r
139 }\r
140 \r
141 /** Matches a non-currency symbol Unicode character. */\r
142 class NUnicodeCurrency\r
143     extends UniValidator\r
144 {\r
145   public int validate(StringLike s, int from, int to)\r
146   {\r
147     return from < s.length() && !Prop.isCurrency(s.charAt(from)) ? to : -1;\r
148   }\r
149 }\r
150 \r
151 /** Matches a Unicode alphabetic character. */\r
152 class UnicodeAlpha\r
153     extends UniValidator\r
154 {\r
155   public int validate(StringLike s, int from, int to)\r
156   {\r
157     return from < s.length() && Prop.isAlphabetic(s.charAt(from)) ? to : -1;\r
158   }\r
159 }\r
160 \r
161 /** Matches a non-alphabetic Unicode character. */\r
162 class NUnicodeAlpha\r
163     extends UniValidator\r
164 {\r
165   public int validate(StringLike s, int from, int to)\r
166   {\r
167     return from < s.length() && !Prop.isAlphabetic(s.charAt(from)) ? to : -1;\r
168   }\r
169 }\r
170 \r
171 /** Matches an upper case Unicode character. */\r
172 class UnicodeUpper\r
173     extends UniValidator\r
174 {\r
175   public int validate(StringLike s, int from, int to)\r
176   {\r
177     return from < s.length() && isUpper(s.charAt(from)) ? to : -1;\r
178   }\r
179 \r
180   final boolean isUpper(char c)\r
181   {\r
182     return c == CaseMgr.toUpperCase(c) && c != CaseMgr.toLowerCase(c);\r
183   }\r
184 }\r
185 \r
186 /** Matches an upper case Unicode character. */\r
187 class UnicodeLower\r
188     extends UniValidator\r
189 {\r
190   public int validate(StringLike s, int from, int to)\r
191   {\r
192     return from < s.length() && isLower(s.charAt(from)) ? to : -1;\r
193   }\r
194 \r
195   final boolean isLower(char c)\r
196   {\r
197     return c != CaseMgr.toUpperCase(c) && c == CaseMgr.toLowerCase(c);\r
198   }\r
199 }\r
200 \r
201 /**\r
202  Regex provides the parser which constructs the linked list of\r
203  Pattern classes from a String.\r
204  <p>\r
205  For the purpose of this documentation, the fact that java interprets the\r
206  backslash will be ignored.  In practice, however, you will need a\r
207  double backslash to obtain a string that contains a single backslash\r
208  character.  Thus, the example pattern "\b" should really be typed\r
209  as "\\b" inside java code.\r
210  <p>\r
211  Note that Regex is part of package "com.stevesoft.pat".\r
212  To use it, simply import\r
213  com.stevesoft.pat.Regex at the top of your file.\r
214  <p>\r
215  Regex is made with a constructor that takes a String that defines\r
216  the regular expression.  Thus, for example\r
217  <pre>\r
218       Regex r = new Regex("[a-c]*");\r
219  </pre>\r
220  matches any number of characters so long as the are 'a', 'b', or 'c').\r
221  <p>\r
222  To attempt to match the Pattern to a given string, you can use either\r
223  the search(String) member function, or the matchAt(String,int position)\r
224  member function.  These functions return a boolean which tells you\r
225  whether or not the thing worked, and sets the methods "charsMatched()"\r
226  and "matchedFrom()" in the Regex object appropriately.\r
227  <p>\r
228  The portion of the string before the match can be obtained by the\r
229  left() member, and the portion after the match can be obtained\r
230  by the right() member.\r
231  <p>\r
232  Essentially, this package implements a syntax that is very much\r
233  like the perl 5 regular expression syntax.\r
234 \r
235  Longer example:\r
236  <pre>\r
237         Regex r = new Regex("x(a|b)y");\r
238         r.matchAt("xay",0);\r
239         System.out.println("sub = "+r.stringMatched(1));\r
240  </pre>\r
241  The above would print "sub = a".\r
242  <pre>\r
243         r.left() // would return "x"\r
244         r.right() // would return "y"\r
245  </pre>\r
246  <p>\r
247  Differences between this package and perl5:<br>\r
248  The extended Pattern for setting flags, is now supported,\r
249  but the flags are different.  "(?i)" tells the pattern to\r
250  ignore case, "(?Q)" sets the "dontMatchInQuotes" flag, and\r
251  "(?iQ)" sets them both.  You can change the escape character.\r
252  The pattern <pre>(?e=#)#d+</pre> is the same as <pre>\d+</pre>,\r
253  but note that the sequence <pre>(?e=#)</pre> <b>must</b> occur\r
254  at the very beginning of the pattern.  There may be other small\r
255  differences as well.  I will either make my package conform\r
256  or note them as I become aware of them.\r
257  <p>\r
258  This package supports additional patterns not in perl5:\r
259  <center>\r
260  <table border=1>\r
261  <tr><td>(?@())</td><td>Group</td><td>This matches all characters between\r
262  the '(' character and the balancing ')' character.  Thus, it will\r
263  match "()" as well as "(())".  The balancing characters are\r
264  arbitrary, thus (?@{}) matches on "{}" and "{{}}".</td>\r
265  <tr><td>(?&lt1)</td><td>Backup</td><td>Moves the pointer backwards within the text.\r
266  This allows you to make a "look behind."  It fails if it\r
267  attempts to move to a position before the beginning of the string.\r
268  "x(?&lt1)" is equivalent to "(?=x)".  The number, 1 in this example,\r
269  is the number of characters to move backwards.</td>\r
270  </table>\r
271  </center>\r
272  </dl>\r
273  @author Steven R. Brandt\r
274  @version package com.stevesoft.pat, release 1.5.3\r
275  @see Pattern\r
276  */\r
277 public class Regex\r
278     extends RegRes implements FilenameFilter\r
279 {\r
280   /** BackRefOffset gives the identity number of the first\r
281       pattern.  Version 1.0 used zero, version 1.1 uses 1 to be\r
282       more compatible with perl. */\r
283   static int BackRefOffset = 1;\r
284   private static Pattern none = new NoPattern();\r
285   Pattern thePattern = none;\r
286   patInt minMatch = new patInt(0);\r
287 \r
288   static Hashtable validators = new Hashtable();\r
289   static\r
290   {\r
291     define("p", "(?>1)", new UnicodePunct());\r
292     define("P", "(?>1)", new NUnicodePunct());\r
293     define("s", "(?>1)", new UnicodeWhite());\r
294     define("S", "(?>1)", new NUnicodeWhite());\r
295     define("w", "(?>1)", new UnicodeW());\r
296     define("W", "(?>1)", new NUnicodeW());\r
297     define("d", "(?>1)", new UnicodeDigit());\r
298     define("D", "(?>1)", new NUnicodeDigit());\r
299     define("m", "(?>1)", new UnicodeMath());\r
300     define("M", "(?>1)", new NUnicodeMath());\r
301     define("c", "(?>1)", new UnicodeCurrency());\r
302     define("C", "(?>1)", new NUnicodeCurrency());\r
303     define("a", "(?>1)", new UnicodeAlpha());\r
304     define("A", "(?>1)", new NUnicodeAlpha());\r
305     define("uc", "(?>1)", new UnicodeUpper());\r
306     define("lc", "(?>1)", new UnicodeLower());\r
307   }\r
308 \r
309   /** Set the dontMatch in quotes flag. */\r
310   public void setDontMatchInQuotes(boolean b)\r
311   {\r
312     dontMatchInQuotes = b;\r
313   }\r
314 \r
315   /** Find out if the dontMatchInQuotes flag is enabled. */\r
316   public boolean getDontMatchInQuotes()\r
317   {\r
318     return dontMatchInQuotes;\r
319   }\r
320 \r
321   boolean dontMatchInQuotes = false;\r
322 \r
323   /** Set the state of the ignoreCase flag.  If set to true, then\r
324       the pattern matcher will ignore case when searching for a\r
325       match. */\r
326   public void setIgnoreCase(boolean b)\r
327   {\r
328     ignoreCase = b;\r
329   }\r
330 \r
331   /** Get the state of the ignoreCase flag.  Returns true if we\r
332       are ignoring the case of the pattern, false otherwise. */\r
333   public boolean getIgnoreCase()\r
334   {\r
335     return ignoreCase;\r
336   }\r
337 \r
338   boolean ignoreCase = false;\r
339 \r
340   static boolean defaultMFlag = false;\r
341   /** Set the default value of the m flag.  If it\r
342       is set to true, then the MFlag will be on\r
343     for any regex search executed. */\r
344   public static void setDefaultMFlag(boolean mFlag)\r
345   {\r
346     defaultMFlag = mFlag;\r
347   }\r
348 \r
349   /** Get the default value of the m flag.  If it\r
350       is set to true, then the MFlag will be on\r
351     for any regex search executed. */\r
352   public static boolean getDefaultMFlag()\r
353   {\r
354     return defaultMFlag;\r
355   }\r
356 \r
357   /** Initializes the object without a Pattern. To supply a Pattern\r
358       use compile(String s).\r
359        @see com.stevesoft.pat.Regex#compile(java.lang.String)\r
360    */\r
361   public Regex()\r
362   {}\r
363 \r
364   /** Create and compile a Regex, but do not throw any exceptions.\r
365       If you wish to have exceptions thrown for syntax errors,\r
366       you must use the Regex(void) constructor to create the\r
367       Regex object, and then call the compile method.  Therefore, you\r
368       should only call this method when you know your pattern is right.\r
369       I will probably become more like\r
370        @see com.stevesoft.pat.Regex#search(java.lang.String)\r
371        @see com.stevesoft.pat.Regex#compile(java.lang.String)\r
372    */\r
373   public Regex(String s)\r
374   {\r
375     try\r
376     {\r
377       compile(s);\r
378     }\r
379     catch (RegSyntax rs)\r
380     {}\r
381   }\r
382 \r
383   ReplaceRule rep = null;\r
384   /** Create and compile both a Regex and a ReplaceRule.\r
385       @see com.stevesoft.pat.ReplaceRule\r
386     @see com.stevesoft.pat.Regex#compile(java.lang.String)\r
387    */\r
388   public Regex(String s, String rp)\r
389   {\r
390     this(s);\r
391     rep = ReplaceRule.perlCode(rp);\r
392   }\r
393 \r
394   /** Create and compile a Regex, but give it the ReplaceRule\r
395       specified.  This allows the user finer control of the\r
396       Replacement process, if that is desired.\r
397       @see com.stevesoft.pat.ReplaceRule\r
398     @see com.stevesoft.pat.Regex#compile(java.lang.String)\r
399    */\r
400   public Regex(String s, ReplaceRule rp)\r
401   {\r
402     this(s);\r
403     rep = rp;\r
404   }\r
405 \r
406   /** Change the ReplaceRule of this Regex by compiling\r
407       a new one using String rp. */\r
408   public void setReplaceRule(String rp)\r
409   {\r
410     rep = ReplaceRule.perlCode(rp);\r
411     repr = null; // Clear Replacer history\r
412   }\r
413 \r
414   /** Change the ReplaceRule of this Regex to rp. */\r
415   public void setReplaceRule(ReplaceRule rp)\r
416   {\r
417     rep = rp;\r
418   }\r
419 \r
420   /** Test to see if a custom defined rule exists.\r
421    @see com.stevesoft.pat#define(java.lang.String,java.lang.String,Validator)\r
422    */\r
423   public static boolean isDefined(String nm)\r
424   {\r
425     return validators.get(nm) != null;\r
426   }\r
427 \r
428   /** Removes a custom defined rule.\r
429    @see com.stevesoft.pat#define(java.lang.String,java.lang.String,Validator)\r
430    */\r
431   public static void undefine(String nm)\r
432   {\r
433     validators.remove(nm);\r
434   }\r
435 \r
436   /** Defines a method to create a new rule.  See test/deriv2.java\r
437       and test/deriv3.java for examples of how to use it. */\r
438   public static void define(String nm, String pat, Validator v)\r
439   {\r
440     v.pattern = pat;\r
441     validators.put(nm, v);\r
442   }\r
443 \r
444   /** Defines a shorthand for a pattern.  The pattern will be\r
445       invoked by a string that has the form "(??"+nm+")".\r
446    */\r
447   public static void define(String nm, String pat)\r
448   {\r
449     validators.put(nm, pat);\r
450   }\r
451 \r
452   /** Get the current ReplaceRule. */\r
453   public ReplaceRule getReplaceRule()\r
454   {\r
455     return rep;\r
456   }\r
457 \r
458   Replacer repr = null;\r
459   final Replacer _getReplacer()\r
460   {\r
461     return repr == null ? repr = new Replacer() : repr;\r
462   }\r
463 \r
464   public Replacer getReplacer()\r
465   {\r
466     if (repr == null)\r
467     {\r
468       repr = new Replacer();\r
469     }\r
470     repr.rh.me = this;\r
471     repr.rh.prev = null;\r
472     return repr;\r
473   }\r
474 \r
475   /** Replace the first occurence of this pattern in String s\r
476       according to the ReplaceRule.\r
477       @see com.stevesoft.pat.ReplaceRule\r
478       @see com.stevesoft.pat.Regex#getReplaceRule()\r
479    */\r
480   public String replaceFirst(String s)\r
481   {\r
482     return _getReplacer().replaceFirstRegion(s, this, 0, s.length()).toString();\r
483   }\r
484 \r
485   /** Replace the first occurence of this pattern in String s\r
486       beginning with position pos according to the ReplaceRule.\r
487       @see com.stevesoft.pat.ReplaceRule\r
488       @see com.stevesoft.pat.Regex#getReplaceRule()\r
489    */\r
490   public String replaceFirstFrom(String s, int pos)\r
491   {\r
492     return _getReplacer().replaceFirstRegion(s, this, pos, s.length()).toString();\r
493   }\r
494 \r
495   /** Replace the first occurence of this pattern in String s\r
496       beginning with position start and ending with end\r
497       according to the ReplaceRule.\r
498       @see com.stevesoft.pat.ReplaceRule\r
499       @see com.stevesoft.pat.Regex#getReplaceRule()\r
500    */\r
501   public String replaceFirstRegion(String s, int start, int end)\r
502   {\r
503     return _getReplacer().replaceFirstRegion(s, this, start, end).toString();\r
504   }\r
505 \r
506   /** Replace all occurences of this pattern in String s\r
507       according to the ReplaceRule.\r
508       @see com.stevesoft.pat.ReplaceRule\r
509       @see com.stevesoft.pat.Regex#getReplaceRule()\r
510    */\r
511   public String replaceAll(String s)\r
512   {\r
513     return _getReplacer().replaceAllRegion(s, this, 0, s.length()).toString();\r
514   }\r
515 \r
516   public StringLike replaceAll(StringLike s)\r
517   {\r
518     return _getReplacer().replaceAllRegion(s, this, 0, s.length());\r
519   }\r
520 \r
521   /** Replace all occurences of this pattern in String s\r
522       beginning with position pos according to the ReplaceRule.\r
523       @see com.stevesoft.pat.ReplaceRule\r
524       @see com.stevesoft.pat.Regex#getReplaceRule()\r
525    */\r
526   public String replaceAllFrom(String s, int pos)\r
527   {\r
528     return _getReplacer().replaceAllRegion(s, this, pos, s.length()).toString();\r
529   }\r
530 \r
531   /** Replace all occurences of this pattern in String s\r
532       beginning with position start and ending with end\r
533       according to the ReplaceRule.\r
534       @see com.stevesoft.pat.ReplaceRule\r
535       @see com.stevesoft.pat.Regex#getReplaceRule()\r
536    */\r
537   public String replaceAllRegion(String s, int start, int end)\r
538   {\r
539     return _getReplacer().replaceAllRegion(s, this, start, end).toString();\r
540   }\r
541 \r
542   /** Essentially clones the Regex object */\r
543   public Regex(Regex r)\r
544   {\r
545     super( (RegRes) r);\r
546     dontMatchInQuotes = r.dontMatchInQuotes;\r
547     esc = r.esc;\r
548     ignoreCase = r.ignoreCase;\r
549     gFlag = r.gFlag;\r
550     if (r.rep == null)\r
551     {\r
552       rep = null;\r
553     }\r
554     else\r
555     {\r
556       rep = (ReplaceRule) r.rep.clone();\r
557     }\r
558     /* try {\r
559         compile(r.toString());\r
560              } catch(RegSyntax r_) {} */\r
561     thePattern = r.thePattern.clone(new Hashtable());\r
562     minMatch = r.minMatch;\r
563     skipper = r.skipper;\r
564   }\r
565 \r
566   /** By default,\r
567               the escape character is the backslash, but you can\r
568               make it anything you want by setting this variable. */\r
569   public char esc = Pattern.ESC;\r
570   /** This method compiles a regular expression, making it\r
571        possible to call the search or matchAt methods.\r
572               @exception com.stevesoft.pat.RegSyntax\r
573               is thrown if a syntax error is encountered\r
574      in the pattern.\r
575                For example, "x{3,1}" or "*a" are not valid\r
576       patterns.\r
577               @see com.stevesoft.pat.Regex#search\r
578               @see com.stevesoft.pat.Regex#matchAt\r
579    */\r
580   public void compile(String prepat)\r
581       throws RegSyntax\r
582   {\r
583     String postpat = parsePerl.codify(prepat, true);\r
584     String pat = postpat == null ? prepat : postpat;\r
585     minMatch = null;\r
586     ignoreCase = false;\r
587     dontMatchInQuotes = false;\r
588     Rthings mk = new Rthings(this);\r
589     int offset = mk.val;\r
590     String newpat = pat;\r
591     thePattern = none;\r
592     p = null;\r
593     or = null;\r
594     minMatch = new patInt(0);\r
595     StrPos sp = new StrPos(pat, 0);\r
596     if (sp.incMatch("(?e="))\r
597     {\r
598       char newEsc = sp.c;\r
599       sp.inc();\r
600       if (sp.match(')'))\r
601       {\r
602         newpat = reEscape(pat.substring(6),\r
603                           newEsc, Pattern.ESC);\r
604       }\r
605     }\r
606     else if (esc != Pattern.ESC)\r
607     {\r
608       newpat = reEscape(pat, esc, Pattern.ESC);\r
609     }\r
610     thePattern = _compile(newpat, mk);\r
611     numSubs_ = mk.val - offset;\r
612     mk.set(this);\r
613   }\r
614 \r
615   /*  If a Regex is compared against a Regex, a check is\r
616       done to see that the patterns are equal as well as\r
617       the most recent match.  If a Regex is compare with\r
618       a RegRes, only the result of the most recent match\r
619       is compared. */\r
620   public boolean equals(Object o)\r
621   {\r
622     if (o instanceof Regex)\r
623     {\r
624       if (toString().equals(o.toString()))\r
625       {\r
626         return super.equals(o);\r
627       }\r
628       else\r
629       {\r
630         return false;\r
631       }\r
632     }\r
633     else\r
634     {\r
635       return super.equals(o);\r
636     }\r
637   }\r
638 \r
639   /** A clone by any other name would smell as sweet. */\r
640   public Object clone()\r
641   {\r
642     return new Regex(this);\r
643   }\r
644 \r
645   /** Return a clone of the underlying RegRes object. */\r
646   public RegRes result()\r
647   {\r
648     return (RegRes)super.clone();\r
649   }\r
650 \r
651   // prep sets global variables of class\r
652   // Pattern so that it can access them\r
653   // during an attempt at a match\r
654   Pthings pt = new Pthings();\r
655   final Pthings prep(StringLike s)\r
656   {\r
657     //if(gFlag)\r
658     pt.lastPos = matchedTo();\r
659     if (pt.lastPos < 0)\r
660     {\r
661       pt.lastPos = 0;\r
662     }\r
663     if ( (s == null ? null : s.unwrap()) != (src == null ? null : s.unwrap()))\r
664     {\r
665       pt.lastPos = 0;\r
666     }\r
667     src = s;\r
668     pt.dotDoesntMatchCR = dotDoesntMatchCR && (!sFlag);\r
669     pt.mFlag = (mFlag | defaultMFlag);\r
670     pt.ignoreCase = ignoreCase;\r
671     pt.no_check = false;\r
672     if (pt.marks != null)\r
673     {\r
674       for (int i = 0; i < pt.marks.length; i++)\r
675       {\r
676         pt.marks[i] = -1;\r
677       }\r
678     }\r
679     pt.marks = null;\r
680     pt.nMarks = numSubs_;\r
681     pt.src = s;\r
682     if (dontMatchInQuotes)\r
683     {\r
684       setCbits(s, pt);\r
685     }\r
686     else\r
687     {\r
688       pt.cbits = null;\r
689     }\r
690     return pt;\r
691   }\r
692 \r
693   /** Attempt to match a Pattern beginning\r
694       at a specified location within the string.\r
695       @see com.stevesoft.pat.Regex#search\r
696    */\r
697   public boolean matchAt(String s, int start_pos)\r
698   {\r
699     return _search(s, start_pos, start_pos);\r
700   }\r
701 \r
702   /** Attempt to match a Pattern beginning\r
703       at a specified location within the StringLike.\r
704       @see com.stevesoft.pat.Regex#search\r
705    */\r
706   public boolean matchAt(StringLike s, int start_pos)\r
707   {\r
708     return _search(s, start_pos, start_pos);\r
709   }\r
710 \r
711   /** Search through a String for the first\r
712       occurrence of a match.\r
713       @see com.stevesoft.pat.Regex#searchFrom\r
714       @see com.stevesoft.pat.Regex#matchAt\r
715    */\r
716   public boolean search(String s)\r
717   {\r
718     if (s == null)\r
719     {\r
720       throw new NullPointerException("Null String Given to Regex.search");\r
721     }\r
722     return _search(s, 0, s.length());\r
723   }\r
724 \r
725   public boolean search(StringLike sl)\r
726   {\r
727     if (sl == null)\r
728     {\r
729       throw new NullPointerException("Null StringLike Given to Regex.search");\r
730     }\r
731     return _search(sl, 0, sl.length());\r
732   }\r
733 \r
734   public boolean reverseSearch(String s)\r
735   {\r
736     if (s == null)\r
737     {\r
738       throw new NullPointerException("Null String Given to Regex.reverseSearch");\r
739     }\r
740     return _reverseSearch(s, 0, s.length());\r
741   }\r
742 \r
743   public boolean reverseSearch(StringLike sl)\r
744   {\r
745     if (sl == null)\r
746     {\r
747       throw new NullPointerException(\r
748           "Null StringLike Given to Regex.reverseSearch");\r
749     }\r
750     return _reverseSearch(sl, 0, sl.length());\r
751   }\r
752 \r
753   /** Search through a String for the first\r
754               occurence of a match, but start at position <pre>start</pre>*/\r
755   public boolean searchFrom(String s, int start)\r
756   {\r
757     if (s == null)\r
758     {\r
759       throw new NullPointerException("Null String Given to Regex.searchFrom");\r
760     }\r
761     return _search(s, start, s.length());\r
762   }\r
763 \r
764   public boolean searchFrom(StringLike s, int start)\r
765   {\r
766     if (s == null)\r
767     {\r
768       throw new NullPointerException("Null String Given to Regex.searchFrom");\r
769     }\r
770     return _search(s, start, s.length());\r
771   }\r
772 \r
773   /** Search through a region of a String\r
774       for the first occurence of a match. */\r
775   public boolean searchRegion(String s, int start, int end)\r
776   {\r
777     if (s == null)\r
778     {\r
779       throw new NullPointerException("Null String Given to Regex.searchRegion");\r
780     }\r
781     return _search(s, start, end);\r
782   }\r
783 \r
784   /** Set this to change the default behavior of the "." pattern.\r
785               By default it now matches perl's behavior and fails to\r
786               match the '\n' character. */\r
787   public static boolean dotDoesntMatchCR = true;\r
788   StringLike gFlags;\r
789   int gFlagto = 0;\r
790   boolean gFlag = false;\r
791   /** Set the 'g' flag */\r
792   public void setGFlag(boolean b)\r
793   {\r
794     gFlag = b;\r
795   }\r
796 \r
797   /** Get the state of the 'g' flag. */\r
798   public boolean getGFlag()\r
799   {\r
800     return gFlag;\r
801   }\r
802 \r
803   boolean sFlag = false;\r
804   /** Get the state of the sFlag */\r
805   public boolean getSFlag()\r
806   {\r
807     return sFlag;\r
808   }\r
809 \r
810   boolean mFlag = false;\r
811   /** Get the state of the sFlag */\r
812   public boolean getMFlag()\r
813   {\r
814     return mFlag;\r
815   }\r
816 \r
817   final boolean _search(String s, int start, int end)\r
818   {\r
819     return _search(new StringWrap(s), start, end);\r
820   }\r
821 \r
822   final boolean _search(StringLike s, int start, int end)\r
823   {\r
824     if (gFlag && gFlagto > 0 && gFlags != null && s.unwrap() == gFlags.unwrap())\r
825     {\r
826       start = gFlagto;\r
827     }\r
828     gFlags = null;\r
829 \r
830     Pthings pt = prep(s);\r
831 \r
832     int up = (minMatch == null ? end : end - minMatch.i);\r
833 \r
834     if (up < start && end >= start)\r
835     {\r
836       up = start;\r
837     }\r
838 \r
839     if (skipper == null)\r
840     {\r
841       for (int i = start; i <= up; i++)\r
842       {\r
843         charsMatched_ = thePattern.matchAt(s, i, pt);\r
844         if (charsMatched_ >= 0)\r
845         {\r
846           matchFrom_ = thePattern.mfrom;\r
847           marks = pt.marks;\r
848           gFlagto = matchFrom_ + charsMatched_;\r
849           gFlags = s;\r
850           return didMatch_ = true;\r
851         }\r
852       }\r
853     }\r
854     else\r
855     {\r
856       pt.no_check = true;\r
857       for (int i = start; i <= up; i++)\r
858       {\r
859         i = skipper.find(src, i, up);\r
860         if (i < 0)\r
861         {\r
862           charsMatched_ = matchFrom_ = -1;\r
863           return didMatch_ = false;\r
864         }\r
865         charsMatched_ = thePattern.matchAt(s, i, pt);\r
866         if (charsMatched_ >= 0)\r
867         {\r
868           matchFrom_ = thePattern.mfrom;\r
869           marks = pt.marks;\r
870           gFlagto = matchFrom_ + charsMatched_;\r
871           gFlags = s;\r
872           return didMatch_ = true;\r
873         }\r
874       }\r
875     }\r
876     return didMatch_ = false;\r
877   }\r
878 \r
879   /*final boolean _search(LongStringLike s,long start,long end) {\r
880       if(gFlag && gFlagto > 0 && s==gFlags)\r
881           start = gFlagto;\r
882       gFlags = null;\r
883 \r
884       Pthings pt=prep(s);\r
885 \r
886       int up = end;//(minMatch == null ? end : end-minMatch.i);\r
887 \r
888       if(up < start && end >= start) up = start;\r
889 \r
890       if(skipper == null) {\r
891           for(long i=start;i<=up;i++) {\r
892               charsMatched_ = thePattern.matchAt(s,i,pt);\r
893               if(charsMatched_ >= 0) {\r
894                   matchFrom_ = thePattern.mfrom;\r
895                   marks = pt.marks;\r
896                   gFlagto = matchFrom_+charsMatched_;\r
897                   return didMatch_=true;\r
898               }\r
899           }\r
900       } else {\r
901           pt.no_check = true;\r
902           for(long i=start;i<=up;i++) {\r
903               i = skipper.find(src,i,up);\r
904               if(i<0) {\r
905                   charsMatched_ = matchFrom_ = -1;\r
906                   return didMatch_ = false;\r
907               }\r
908               charsMatched_ = thePattern.matchAt(s,i,pt);\r
909               if(charsMatched_ >= 0) {\r
910                   matchFrom_ = thePattern.mfrom;\r
911                   marks = pt.marks;\r
912                   gFlagto = matchFrom_+charsMatched_;\r
913                   gFlags = s;\r
914                   return didMatch_=true;\r
915               } else {\r
916                 i = s.adjustIndex(i);\r
917                 up = s.adjustEnd(i);\r
918               }\r
919           }\r
920       }\r
921       return didMatch_=false;\r
922        }*/\r
923 \r
924   boolean _reverseSearch(String s, int start, int end)\r
925   {\r
926     return _reverseSearch(new StringWrap(s), start, end);\r
927   }\r
928 \r
929   boolean _reverseSearch(StringLike s, int start, int end)\r
930   {\r
931     if (gFlag && gFlagto > 0 && s.unwrap() == gFlags.unwrap())\r
932     {\r
933       end = gFlagto;\r
934     }\r
935     gFlags = null;\r
936     Pthings pt = prep(s);\r
937     for (int i = end; i >= start; i--)\r
938     {\r
939       charsMatched_ = thePattern.matchAt(s, i, pt);\r
940       if (charsMatched_ >= 0)\r
941       {\r
942         matchFrom_ = thePattern.mfrom;\r
943         marks = pt.marks;\r
944         gFlagto = matchFrom_ - 1;\r
945         gFlags = s;\r
946         return didMatch_ = true;\r
947       }\r
948     }\r
949     return didMatch_ = false;\r
950   }\r
951 \r
952   // This routine sets the cbits variable\r
953   // of class Pattern.  Cbits is true for\r
954   // the bit corresponding to a character inside\r
955   // a set of quotes.\r
956   static StringLike lasts = null;\r
957   static BitSet lastbs = null;\r
958   static void setCbits(StringLike s, Pthings pt)\r
959   {\r
960     if (s == lasts)\r
961     {\r
962       pt.cbits = lastbs;\r
963       return;\r
964     }\r
965     BitSet bs = new BitSet(s.length());\r
966     char qc = ' ';\r
967     boolean setBit = false;\r
968     for (int i = 0; i < s.length(); i++)\r
969     {\r
970       if (setBit)\r
971       {\r
972         bs.set(i);\r
973       }\r
974       char c = s.charAt(i);\r
975       if (!setBit && c == '"')\r
976       {\r
977         qc = c;\r
978         setBit = true;\r
979         bs.set(i);\r
980       }\r
981       else if (!setBit && c == '\'')\r
982       {\r
983         qc = c;\r
984         setBit = true;\r
985         bs.set(i);\r
986       }\r
987       else if (setBit && c == qc)\r
988       {\r
989         setBit = false;\r
990       }\r
991       else if (setBit && c == '\\' && i + 1 < s.length())\r
992       {\r
993         i++;\r
994         if (setBit)\r
995         {\r
996           bs.set(i);\r
997         }\r
998       }\r
999     }\r
1000     pt.cbits = lastbs = bs;\r
1001     lasts = s;\r
1002   }\r
1003 \r
1004   // Wanted user to over-ride this in alpha version,\r
1005   // but it wasn't really necessary because of this trick:\r
1006   Regex newRegex()\r
1007   {\r
1008     try\r
1009     {\r
1010       return (Regex) getClass().newInstance();\r
1011     }\r
1012     catch (InstantiationException ie)\r
1013     {\r
1014       return null;\r
1015     }\r
1016     catch (IllegalAccessException iae)\r
1017     {\r
1018       return null;\r
1019     }\r
1020   }\r
1021 \r
1022   /** Only needed for creating your own extensions of\r
1023        Regex.  This method adds the next Pattern in the chain\r
1024        of patterns or sets the Pattern if it is the first call. */\r
1025   protected void add(Pattern p2)\r
1026   {\r
1027     if (p == null)\r
1028     {\r
1029       p = p2;\r
1030     }\r
1031     else\r
1032     {\r
1033       p.add(p2);\r
1034       p2 = p;\r
1035     }\r
1036   }\r
1037 \r
1038   /** You only need to use this method if you are creating\r
1039       your own extentions to Regex.\r
1040       compile1 compiles one Pattern element, it can be\r
1041       over-ridden to allow the Regex compiler to understand\r
1042       new syntax.  See deriv.java for an example.  This routine\r
1043       is the heart of class Regex. Rthings has one integer\r
1044       member called intValue, it is used to keep track of the number\r
1045       of ()'s in the Pattern.\r
1046       @exception com.stevesoft.pat.RegSyntax is thrown when a nonsensensical\r
1047       pattern is supplied.  For example, a pattern beginning\r
1048       with *. */\r
1049   protected void compile1(StrPos sp, Rthings mk)\r
1050       throws RegSyntax\r
1051   {\r
1052     if (sp.match('['))\r
1053     {\r
1054       sp.inc();\r
1055       add(matchBracket(sp));\r
1056     }\r
1057     else if (sp.match('|'))\r
1058     {\r
1059       if (or == null)\r
1060       {\r
1061         or = new Or();\r
1062       }\r
1063       if (p == null)\r
1064       {\r
1065         p = new NullPattern();\r
1066       }\r
1067       or.addOr(p);\r
1068       p = null;\r
1069     }\r
1070     else if (sp.incMatch("(?<"))\r
1071     {\r
1072       patInt i = sp.getPatInt();\r
1073       if (i == null)\r
1074       {\r
1075         RegSyntaxError.endItAll("No int after (?<");\r
1076       }\r
1077       add(new Backup(i.intValue()));\r
1078       if (!sp.match(')'))\r
1079       {\r
1080         RegSyntaxError.endItAll("No ) after (?<");\r
1081       }\r
1082     }\r
1083     else if (sp.incMatch("(?>"))\r
1084     {\r
1085       patInt i = sp.getPatInt();\r
1086       if (i == null)\r
1087       {\r
1088         RegSyntaxError.endItAll("No int after (?>");\r
1089       }\r
1090       add(new Backup( -i.intValue()));\r
1091       if (!sp.match(')'))\r
1092       {\r
1093         RegSyntaxError.endItAll("No ) after (?<");\r
1094       }\r
1095     }\r
1096     else if (sp.incMatch("(?@"))\r
1097     {\r
1098       char op = sp.c;\r
1099       sp.inc();\r
1100       char cl = sp.c;\r
1101       sp.inc();\r
1102       if (!sp.match(')'))\r
1103       {\r
1104         RegSyntaxError.endItAll(\r
1105             "(?@ does not have closing paren");\r
1106       }\r
1107       add(new Group(op, cl));\r
1108     }\r
1109     else if (sp.incMatch("(?#"))\r
1110     {\r
1111       while (!sp.match(')'))\r
1112       {\r
1113         sp.inc();\r
1114       }\r
1115     }\r
1116     else if (sp.dontMatch && sp.c == 'w')\r
1117     {\r
1118       //Regex r = new Regex();\r
1119       //r._compile("[a-zA-Z0-9_]",mk);\r
1120       //add(new Goop("\\w",r.thePattern));\r
1121       Bracket b = new Bracket(false);\r
1122       b.addOr(new Range('a', 'z'));\r
1123       b.addOr(new Range('A', 'Z'));\r
1124       b.addOr(new Range('0', '9'));\r
1125       b.addOr(new oneChar('_'));\r
1126       add(b);\r
1127     }\r
1128     else if (sp.dontMatch && sp.c == 'G')\r
1129     {\r
1130       add(new BackG());\r
1131     }\r
1132     else if (sp.dontMatch && sp.c == 's')\r
1133     {\r
1134       //Regex r = new Regex();\r
1135       //r._compile("[ \t\n\r\b]",mk);\r
1136       //add(new Goop("\\s",r.thePattern));\r
1137       Bracket b = new Bracket(false);\r
1138       b.addOr(new oneChar( (char) 32));\r
1139       b.addOr(new Range( (char) 8, (char) 10));\r
1140       b.addOr(new oneChar( (char) 13));\r
1141       add(b);\r
1142     }\r
1143     else if (sp.dontMatch && sp.c == 'd')\r
1144     {\r
1145       //Regex r = new Regex();\r
1146       //r._compile("[0-9]",mk);\r
1147       //add(new Goop("\\d",r.thePattern));\r
1148       Range digit = new Range('0', '9');\r
1149       digit.printBrackets = true;\r
1150       add(digit);\r
1151     }\r
1152     else if (sp.dontMatch && sp.c == 'W')\r
1153     {\r
1154       //Regex r = new Regex();\r
1155       //r._compile("[^a-zA-Z0-9_]",mk);\r
1156       //add(new Goop("\\W",r.thePattern));\r
1157       Bracket b = new Bracket(true);\r
1158       b.addOr(new Range('a', 'z'));\r
1159       b.addOr(new Range('A', 'Z'));\r
1160       b.addOr(new Range('0', '9'));\r
1161       b.addOr(new oneChar('_'));\r
1162       add(b);\r
1163     }\r
1164     else if (sp.dontMatch && sp.c == 'S')\r
1165     {\r
1166       //Regex r = new Regex();\r
1167       //r._compile("[^ \t\n\r\b]",mk);\r
1168       //add(new Goop("\\S",r.thePattern));\r
1169       Bracket b = new Bracket(true);\r
1170       b.addOr(new oneChar( (char) 32));\r
1171       b.addOr(new Range( (char) 8, (char) 10));\r
1172       b.addOr(new oneChar( (char) 13));\r
1173       add(b);\r
1174     }\r
1175     else if (sp.dontMatch && sp.c == 'D')\r
1176     {\r
1177       //Regex r = new Regex();\r
1178       //r._compile("[^0-9]",mk);\r
1179       //add(new Goop("\\D",r.thePattern));\r
1180       Bracket b = new Bracket(true);\r
1181       b.addOr(new Range('0', '9'));\r
1182       add(b);\r
1183     }\r
1184     else if (sp.dontMatch && sp.c == 'B')\r
1185     {\r
1186       Regex r = new Regex();\r
1187       r._compile("(?!" + back_slash + "b)", mk);\r
1188       add(r.thePattern);\r
1189     }\r
1190     else if (isOctalString(sp))\r
1191     {\r
1192       int d = sp.c - '0';\r
1193       sp.inc();\r
1194       d = 8 * d + sp.c - '0';\r
1195       StrPos sp2 = new StrPos(sp);\r
1196       sp2.inc();\r
1197       if (isOctalDigit(sp2, false))\r
1198       {\r
1199         sp.inc();\r
1200         d = 8 * d + sp.c - '0';\r
1201       }\r
1202       add(new oneChar( (char) d));\r
1203     }\r
1204     else if (sp.dontMatch && sp.c >= '1' && sp.c <= '9')\r
1205     {\r
1206       int iv = sp.c - '0';\r
1207       StrPos s2 = new StrPos(sp);\r
1208       s2.inc();\r
1209       if (!s2.dontMatch && s2.c >= '0' && s2.c <= '9')\r
1210       {\r
1211         iv = 10 * iv + (s2.c - '0');\r
1212         sp.inc();\r
1213       }\r
1214       add(new BackMatch(iv));\r
1215     }\r
1216     else if (sp.dontMatch && sp.c == 'b')\r
1217     {\r
1218       add(new Boundary());\r
1219     }\r
1220     else if (sp.match('\b'))\r
1221     {\r
1222       add(new Boundary());\r
1223     }\r
1224     else if (sp.match('$'))\r
1225     {\r
1226       add(new End(true));\r
1227     }\r
1228     else if (sp.dontMatch && sp.c == 'Z')\r
1229     {\r
1230       add(new End(false));\r
1231     }\r
1232     else if (sp.match('.'))\r
1233     {\r
1234       add(new Any());\r
1235     }\r
1236     else if (sp.incMatch("(??"))\r
1237     {\r
1238       StringBuffer sb = new StringBuffer();\r
1239       StringBuffer sb2 = new StringBuffer();\r
1240       while (!sp.match(')') && !sp.match(':'))\r
1241       {\r
1242         sb.append(sp.c);\r
1243         sp.inc();\r
1244       }\r
1245       if (sp.incMatch(":"))\r
1246       {\r
1247         while (!sp.match(')'))\r
1248         {\r
1249           sb2.append(sp.c);\r
1250           sp.inc();\r
1251         }\r
1252       }\r
1253       String sbs = sb.toString();\r
1254       if (validators.get(sbs) instanceof String)\r
1255       {\r
1256         String pat = (String) validators.get(sbs);\r
1257         Regex r = newRegex();\r
1258         Rthings rth = new Rthings(this);\r
1259         rth.noBackRefs = true;\r
1260         r._compile(pat, rth);\r
1261         add(r.thePattern);\r
1262       }\r
1263       else\r
1264       {\r
1265         Custom cm = new Custom(sb.toString());\r
1266         if (cm.v != null)\r
1267         {\r
1268           Validator v2 = cm.v.arg(sb2.toString());\r
1269           if (v2 != null)\r
1270           {\r
1271             v2.argsave = sb2.toString();\r
1272             String p = cm.v.pattern;\r
1273             cm.v = v2;\r
1274             v2.pattern = p;\r
1275           }\r
1276           Regex r = newRegex();\r
1277           Rthings rth = new Rthings(this);\r
1278           rth.noBackRefs = true;\r
1279           r._compile(cm.v.pattern, rth);\r
1280           cm.sub = r.thePattern;\r
1281           cm.sub.add(new CustomEndpoint(cm));\r
1282           cm.sub.setParent(cm);\r
1283           add(cm);\r
1284         }\r
1285       }\r
1286     }\r
1287     else if (sp.match('('))\r
1288     {\r
1289       mk.parenLevel++;\r
1290       Regex r = newRegex();\r
1291       // r.or = new Or();\r
1292       sp.inc();\r
1293       if (sp.incMatch("?:"))\r
1294       {\r
1295         r.or = new Or();\r
1296       }\r
1297       else if (sp.incMatch("?="))\r
1298       {\r
1299         r.or = new lookAhead(false);\r
1300       }\r
1301       else if (sp.incMatch("?!"))\r
1302       {\r
1303         r.or = new lookAhead(true);\r
1304       }\r
1305       else if (sp.match('?'))\r
1306       {\r
1307         sp.inc();\r
1308         do\r
1309         {\r
1310           if (sp.c == 'i')\r
1311           {\r
1312             mk.ignoreCase = true;\r
1313           }\r
1314           if (sp.c == 'Q')\r
1315           {\r
1316             mk.dontMatchInQuotes = true;\r
1317           }\r
1318           if (sp.c == 'o')\r
1319           {\r
1320             mk.optimizeMe = true;\r
1321           }\r
1322           if (sp.c == 'g')\r
1323           {\r
1324             mk.gFlag = true;\r
1325           }\r
1326           if (sp.c == 's')\r
1327           {\r
1328             mk.sFlag = true;\r
1329           }\r
1330           if (sp.c == 'm')\r
1331           {\r
1332             mk.mFlag = true;\r
1333           }\r
1334           sp.inc();\r
1335         }\r
1336         while (!sp.match(')') && !sp.eos);\r
1337         r = null;\r
1338         mk.parenLevel--;\r
1339         if (sp.eos) //throw new RegSyntax\r
1340         {\r
1341           RegSyntaxError.endItAll("Unclosed ()");\r
1342         }\r
1343       }\r
1344       else\r
1345       { // just ordinary parenthesis\r
1346         r.or = mk.noBackRefs ? new Or() : new OrMark(mk.val++);\r
1347       }\r
1348       if (r != null)\r
1349       {\r
1350         add(r._compile(sp, mk));\r
1351       }\r
1352     }\r
1353     else if (sp.match('^'))\r
1354     {\r
1355       add(new Start(true));\r
1356     }\r
1357     else if (sp.dontMatch && sp.c == 'A')\r
1358     {\r
1359       add(new Start(false));\r
1360     }\r
1361     else if (sp.match('*'))\r
1362     {\r
1363       addMulti(new patInt(0), new patInf());\r
1364     }\r
1365     else if (sp.match('+'))\r
1366     {\r
1367       addMulti(new patInt(1), new patInf());\r
1368     }\r
1369     else if (sp.match('?'))\r
1370     {\r
1371       addMulti(new patInt(0), new patInt(1));\r
1372     }\r
1373     else if (sp.match('{'))\r
1374     {\r
1375       boolean bad = false;\r
1376       StrPos sp2 = new StrPos(sp);\r
1377       //StringBuffer sb = new StringBuffer();\r
1378       sp.inc();\r
1379       patInt i1 = sp.getPatInt();\r
1380       patInt i2 = null;\r
1381       if (sp.match('}'))\r
1382       {\r
1383         i2 = i1;\r
1384       }\r
1385       else\r
1386       {\r
1387         if (!sp.match(',')) /*\r
1388          {\r
1389                                RegSyntaxError.endItAll(\r
1390                                   "String \"{"+i2+\r
1391                                   "\" should be followed with , or }");*/\r
1392           bad = true;\r
1393          }\r
1394         sp.inc();\r
1395         if (sp.match('}'))\r
1396         {\r
1397           i2 = new patInf();\r
1398         }\r
1399         else\r
1400         {\r
1401           i2 = sp.getPatInt();\r
1402         }\r
1403       }\r
1404       if (i1 == null || i2 == null) /*\r
1405        {\r
1406          throw new RegSyntax("Badly formatted Multi: "\r
1407                                          +"{"+i1+","+i2+"}"); */bad = true;\r
1408        }\r
1409       if (bad)\r
1410       {\r
1411         sp.dup(sp2);\r
1412         add(new oneChar(sp.c));\r
1413       }\r
1414       else\r
1415       {\r
1416         addMulti(i1, i2);\r
1417       }\r
1418     }\r
1419     else if (sp.escMatch('x') && next2Hex(sp))\r
1420     {\r
1421       sp.inc();\r
1422       int d = getHexDigit(sp);\r
1423       sp.inc();\r
1424       d = 16 * d + getHexDigit(sp);\r
1425       add(new oneChar( (char) d));\r
1426     }\r
1427     else if (sp.escMatch('c'))\r
1428     {\r
1429       sp.inc();\r
1430       if (sp.c < Ctrl.cmap.length)\r
1431       {\r
1432         add(new oneChar(Ctrl.cmap[sp.c]));\r
1433       }\r
1434       else\r
1435       {\r
1436         add(new oneChar(sp.c));\r
1437       }\r
1438     }\r
1439     else if (sp.escMatch('f'))\r
1440     {\r
1441       add(new oneChar( (char) 12));\r
1442     }\r
1443     else if (sp.escMatch('a'))\r
1444     {\r
1445       add(new oneChar( (char) 7));\r
1446     }\r
1447     else if (sp.escMatch('t'))\r
1448     {\r
1449       add(new oneChar('\t'));\r
1450     }\r
1451     else if (sp.escMatch('n'))\r
1452     {\r
1453       add(new oneChar('\n'));\r
1454     }\r
1455     else if (sp.escMatch('r'))\r
1456     {\r
1457       add(new oneChar('\r'));\r
1458     }\r
1459     else if (sp.escMatch('b'))\r
1460     {\r
1461       add(new oneChar('\b'));\r
1462     }\r
1463     else if (sp.escMatch('e'))\r
1464     {\r
1465       add(new oneChar( (char) 27));\r
1466     }\r
1467     else\r
1468     {\r
1469       add(new oneChar(sp.c));\r
1470       if (sp.match(')'))\r
1471       {\r
1472         RegSyntaxError.endItAll("Unmatched right paren in pattern");\r
1473       }\r
1474     }\r
1475   }\r
1476 \r
1477   // compiles all Pattern elements, internal method\r
1478   private Pattern _compile(String pat, Rthings mk)\r
1479       throws RegSyntax\r
1480   {\r
1481     minMatch = null;\r
1482     sFlag = mFlag = ignoreCase = gFlag = false;\r
1483     StrPos sp = new StrPos(pat, 0);\r
1484     thePattern = _compile(sp, mk);\r
1485     pt.marks = null;\r
1486     return thePattern;\r
1487   }\r
1488 \r
1489   Pattern p = null;\r
1490   Or or = null;\r
1491   Pattern _compile(StrPos sp, Rthings mk)\r
1492       throws RegSyntax\r
1493   {\r
1494     while (! (sp.eos || (or != null && sp.match(')'))))\r
1495     {\r
1496       compile1(sp, mk);\r
1497       sp.inc();\r
1498     }\r
1499     if (sp.match(')'))\r
1500     {\r
1501       mk.parenLevel--;\r
1502     }\r
1503     else if (sp.eos && mk.parenLevel != 0)\r
1504     {\r
1505       RegSyntaxError.endItAll("Unclosed Parenthesis! lvl=" + mk.parenLevel);\r
1506     }\r
1507     if (or != null)\r
1508     {\r
1509       if (p == null)\r
1510       {\r
1511         p = new NullPattern();\r
1512       }\r
1513       or.addOr(p);\r
1514       return or;\r
1515     }\r
1516     return p == null ? new NullPattern() : p;\r
1517   }\r
1518 \r
1519   // add a multi object to the end of the chain\r
1520   // which applies to the last object\r
1521   void addMulti(patInt i1, patInt i2)\r
1522       throws RegSyntax\r
1523   {\r
1524     Pattern last, last2;\r
1525     for (last = p; last != null && last.next != null; last = last.next)\r
1526     {\r
1527       ;\r
1528     }\r
1529     if (last == null || last == p)\r
1530     {\r
1531       last2 = null;\r
1532     }\r
1533     else\r
1534     {\r
1535       for (last2 = p; last2.next != last; last2 = last2.next)\r
1536       {\r
1537         ;\r
1538       }\r
1539     }\r
1540     if (last instanceof Multi && i1.intValue() == 0 &&\r
1541         i2.intValue() == 1)\r
1542     {\r
1543       ( (Multi) last).matchFewest = true;\r
1544     }\r
1545     else if (last instanceof FastMulti && i1.intValue() == 0 &&\r
1546              i2.intValue() == 1)\r
1547     {\r
1548       ( (FastMulti) last).matchFewest = true;\r
1549     }\r
1550     else if (last instanceof DotMulti && i1.intValue() == 0 &&\r
1551              i2.intValue() == 1)\r
1552     {\r
1553       ( (DotMulti) last).matchFewest = true;\r
1554     }\r
1555     else if (last instanceof Multi\r
1556              || last instanceof DotMulti\r
1557              || last instanceof FastMulti)\r
1558     {\r
1559       throw new RegSyntax("Syntax error.");\r
1560     }\r
1561     else if (last2 == null)\r
1562     {\r
1563       p = mkMulti(i1, i2, p);\r
1564     }\r
1565     else\r
1566     {\r
1567       last2.next = mkMulti(i1, i2, last);\r
1568     }\r
1569   }\r
1570 \r
1571   final static Pattern mkMulti(patInt lo, patInt hi, Pattern p)\r
1572       throws RegSyntax\r
1573   {\r
1574     if (p instanceof Any && p.next == null)\r
1575     {\r
1576       return (Pattern)new DotMulti(lo, hi);\r
1577     }\r
1578     return RegOpt.safe4fm(p) ? (Pattern)new FastMulti(lo, hi, p) :\r
1579         (Pattern)new Multi(lo, hi, p);\r
1580   }\r
1581 \r
1582   // process the bracket operator\r
1583   Pattern matchBracket(StrPos sp)\r
1584       throws RegSyntax\r
1585   {\r
1586     Bracket ret;\r
1587     if (sp.match('^'))\r
1588     {\r
1589       ret = new Bracket(true);\r
1590       sp.inc();\r
1591     }\r
1592     else\r
1593     {\r
1594       ret = new Bracket(false);\r
1595     }\r
1596     if (sp.match(']'))\r
1597     {\r
1598       //throw new RegSyntax\r
1599       RegSyntaxError.endItAll("Unmatched []");\r
1600     }\r
1601 \r
1602     while (!sp.eos && !sp.match(']'))\r
1603     {\r
1604       StrPos s1 = new StrPos(sp);\r
1605       s1.inc();\r
1606       StrPos s1_ = new StrPos(s1);\r
1607       s1_.inc();\r
1608       if (s1.match('-') && !s1_.match(']'))\r
1609       {\r
1610         StrPos s2 = new StrPos(s1);\r
1611         s2.inc();\r
1612         if (!s2.eos)\r
1613         {\r
1614           ret.addOr(new Range(sp.c, s2.c));\r
1615         }\r
1616         sp.inc();\r
1617         sp.inc();\r
1618       }\r
1619       else if (sp.escMatch('Q'))\r
1620       {\r
1621         sp.inc();\r
1622         while (!sp.escMatch('E'))\r
1623         {\r
1624           ret.addOr(new oneChar(sp.c));\r
1625           sp.inc();\r
1626         }\r
1627       }\r
1628       else if (sp.escMatch('d'))\r
1629       {\r
1630         ret.addOr(new Range('0', '9'));\r
1631       }\r
1632       else if (sp.escMatch('s'))\r
1633       {\r
1634         ret.addOr(new oneChar( (char) 32));\r
1635         ret.addOr(new Range( (char) 8, (char) 10));\r
1636         ret.addOr(new oneChar( (char) 13));\r
1637       }\r
1638       else if (sp.escMatch('w'))\r
1639       {\r
1640         ret.addOr(new Range('a', 'z'));\r
1641         ret.addOr(new Range('A', 'Z'));\r
1642         ret.addOr(new Range('0', '9'));\r
1643         ret.addOr(new oneChar('_'));\r
1644       }\r
1645       else if (sp.escMatch('D'))\r
1646       {\r
1647         ret.addOr(new Range( (char) 0, (char) 47));\r
1648         ret.addOr(new Range( (char) 58, (char) 65535));\r
1649       }\r
1650       else if (sp.escMatch('S'))\r
1651       {\r
1652         ret.addOr(new Range( (char) 0, (char) 7));\r
1653         ret.addOr(new Range( (char) 11, (char) 12));\r
1654         ret.addOr(new Range( (char) 14, (char) 31));\r
1655         ret.addOr(new Range( (char) 33, (char) 65535));\r
1656       }\r
1657       else if (sp.escMatch('W'))\r
1658       {\r
1659         ret.addOr(new Range( (char) 0, (char) 64));\r
1660         ret.addOr(new Range( (char) 91, (char) 94));\r
1661         ret.addOr(new oneChar( (char) 96));\r
1662         ret.addOr(new Range( (char) 123, (char) 65535));\r
1663       }\r
1664       else if (sp.escMatch('x') && next2Hex(sp))\r
1665       {\r
1666         sp.inc();\r
1667         int d = getHexDigit(sp);\r
1668         sp.inc();\r
1669         d = 16 * d + getHexDigit(sp);\r
1670         ret.addOr(new oneChar( (char) d));\r
1671       }\r
1672       else if (sp.escMatch('a'))\r
1673       {\r
1674         ret.addOr(new oneChar( (char) 7));\r
1675       }\r
1676       else if (sp.escMatch('f'))\r
1677       {\r
1678         ret.addOr(new oneChar( (char) 12));\r
1679       }\r
1680       else if (sp.escMatch('e'))\r
1681       {\r
1682         ret.addOr(new oneChar( (char) 27));\r
1683       }\r
1684       else if (sp.escMatch('n'))\r
1685       {\r
1686         ret.addOr(new oneChar('\n'));\r
1687       }\r
1688       else if (sp.escMatch('t'))\r
1689       {\r
1690         ret.addOr(new oneChar('\t'));\r
1691       }\r
1692       else if (sp.escMatch('r'))\r
1693       {\r
1694         ret.addOr(new oneChar('\r'));\r
1695       }\r
1696       else if (sp.escMatch('c'))\r
1697       {\r
1698         sp.inc();\r
1699         if (sp.c < Ctrl.cmap.length)\r
1700         {\r
1701           ret.addOr(new oneChar(Ctrl.cmap[sp.c]));\r
1702         }\r
1703         else\r
1704         {\r
1705           ret.addOr(new oneChar(sp.c));\r
1706         }\r
1707       }\r
1708       else if (isOctalString(sp))\r
1709       {\r
1710         int d = sp.c - '0';\r
1711         sp.inc();\r
1712         d = 8 * d + sp.c - '0';\r
1713         StrPos sp2 = new StrPos(sp);\r
1714         sp2.inc();\r
1715         if (isOctalDigit(sp2, false))\r
1716         {\r
1717           sp.inc();\r
1718           d = 8 * d + sp.c - '0';\r
1719         }\r
1720         ret.addOr(new oneChar( (char) d));\r
1721       }\r
1722       else\r
1723       {\r
1724         ret.addOr(new oneChar(sp.c));\r
1725       }\r
1726       sp.inc();\r
1727     }\r
1728     return ret;\r
1729   }\r
1730 \r
1731   /** Converts the stored Pattern to a String -- this is a\r
1732         decompile.  Note that \t and \n will really print out here,\r
1733         Not just the two character representations.\r
1734         Also be prepared to see some strange output if your characters\r
1735         are not printable. */\r
1736   public String toString()\r
1737   {\r
1738     if (false && thePattern == null)\r
1739     {\r
1740       return "";\r
1741     }\r
1742     else\r
1743     {\r
1744       StringBuffer sb = new StringBuffer();\r
1745       if (esc != Pattern.ESC)\r
1746       {\r
1747         sb.append("(?e=");\r
1748         sb.append(esc);\r
1749         sb.append(")");\r
1750       }\r
1751       if (gFlag\r
1752           || mFlag\r
1753           || !dotDoesntMatchCR\r
1754           || sFlag\r
1755           || ignoreCase\r
1756           || dontMatchInQuotes\r
1757           || optimized())\r
1758       {\r
1759         sb.append("(?");\r
1760         if (ignoreCase)\r
1761         {\r
1762           sb.append("i");\r
1763         }\r
1764         if (mFlag)\r
1765         {\r
1766           sb.append("m");\r
1767         }\r
1768         if (sFlag || !dotDoesntMatchCR)\r
1769         {\r
1770           sb.append("s");\r
1771         }\r
1772         if (dontMatchInQuotes)\r
1773         {\r
1774           sb.append("Q");\r
1775         }\r
1776         if (optimized())\r
1777         {\r
1778           sb.append("o");\r
1779         }\r
1780         if (gFlag)\r
1781         {\r
1782           sb.append("g");\r
1783         }\r
1784         sb.append(")");\r
1785       }\r
1786       String patstr = thePattern.toString();\r
1787       if (esc != Pattern.ESC)\r
1788       {\r
1789         patstr = reEscape(patstr, Pattern.ESC, esc);\r
1790       }\r
1791       sb.append(patstr);\r
1792       return sb.toString();\r
1793     }\r
1794   }\r
1795 \r
1796   // Re-escape Pattern, allows us to use a different escape\r
1797   // character.\r
1798   static String reEscape(String s, char oldEsc, char newEsc)\r
1799   {\r
1800     if (oldEsc == newEsc)\r
1801     {\r
1802       return s;\r
1803     }\r
1804     int i;\r
1805     StringBuffer sb = new StringBuffer();\r
1806     for (i = 0; i < s.length(); i++)\r
1807     {\r
1808       if (s.charAt(i) == oldEsc && i + 1 < s.length())\r
1809       {\r
1810         if (s.charAt(i + 1) == oldEsc)\r
1811         {\r
1812           sb.append(oldEsc);\r
1813         }\r
1814         else\r
1815         {\r
1816           sb.append(newEsc);\r
1817           sb.append(s.charAt(i + 1));\r
1818         }\r
1819         i++;\r
1820       }\r
1821       else if (s.charAt(i) == newEsc)\r
1822       {\r
1823         sb.append(newEsc);\r
1824         sb.append(newEsc);\r
1825       }\r
1826       else\r
1827       {\r
1828         sb.append(s.charAt(i));\r
1829       }\r
1830     }\r
1831     return sb.toString();\r
1832   }\r
1833 \r
1834   /** This method implements FilenameFilter, allowing one\r
1835        to use a Regex to search through a directory using File.list.\r
1836        There is a FileRegex now that does this better.\r
1837        @see com.stevesoft.pat.FileRegex\r
1838    */\r
1839   public boolean accept(File dir, String s)\r
1840   {\r
1841     return search(s);\r
1842   }\r
1843 \r
1844   /** The version of this package */\r
1845   final static public String version()\r
1846   {\r
1847     return "lgpl release 1.5.3";\r
1848   }\r
1849 \r
1850   /** Once this method is called, the state of variables\r
1851       ignoreCase and dontMatchInQuotes should not be changed as the\r
1852       results will be unpredictable.  However,\r
1853       search and matchAt will run more quickly.  Note that you\r
1854       can check to see if the pattern has been optimized by calling\r
1855       the optimized() method.<p>This method will attempt to rewrite\r
1856       your pattern in a way that makes it faster (not all patterns\r
1857       execute at the same speed).  In general, "(?: ... )" will be\r
1858       faster than "( ... )" so if you don't need the backreference,\r
1859       you should group using the former pattern.<p>It will also\r
1860       introduce new pattern elements that you can't get to otherwise,\r
1861       for example if you have a large table of strings, i.e. the\r
1862       months of the year "(January|February|...)" optimize() will make\r
1863       a Hashtable that takes it to the next appropriate pattern\r
1864       element -- eliminating the need for a linear search.\r
1865       @see com.stevesoft.pat.Regex#optimized\r
1866       @see com.stevesoft.pat.Regex#ignoreCase\r
1867       @see com.stevesoft.pat.Regex#dontMatchInQuotes\r
1868       @see com.stevesoft.pat.Regex#matchAt\r
1869       @see com.stevesoft.pat.Regex#search\r
1870    */\r
1871   public void optimize()\r
1872   {\r
1873     if (optimized() || thePattern == null)\r
1874     {\r
1875       return;\r
1876     }\r
1877     minMatch = new patInt(0); //thePattern.countMinChars();\r
1878     thePattern = RegOpt.opt(thePattern, ignoreCase,\r
1879                             dontMatchInQuotes);\r
1880     skipper = Skip.findSkip(this);\r
1881     //RegOpt.setParents(this);\r
1882     return;\r
1883   }\r
1884 \r
1885   Skip skipper;\r
1886   /** This function returns true if the optimize method has\r
1887        been called. */\r
1888   public boolean optimized()\r
1889   {\r
1890     return minMatch != null;\r
1891   }\r
1892 \r
1893   /** A bit of syntactic surgar for those who want to make\r
1894       their code look more perl-like.  To use this initialize\r
1895       your Regex object by saying:\r
1896       <pre>\r
1897       Regex r1 = Regex.perlCode("s/hello/goodbye/");\r
1898       Regex r2 = Regex.perlCode("s'fish'frog'i");\r
1899       Regex r3 = Regex.perlCode("m'hello');\r
1900       </pre>\r
1901       The i for ignoreCase is supported in\r
1902       this syntax, as well as m, s, and x.  The g flat\r
1903     is a bit of a special case.<p>\r
1904     If you wish to replace all occurences of a pattern, you\r
1905       do not put a 'g' in the perlCode, but call Regex's\r
1906       replaceAll method.<p>\r
1907     If you wish to simply\r
1908       and only do a search for r2's pattern, you can do this\r
1909       by calling the searchFrom method method repeatedly, or\r
1910     by calling search repeatedly if the g flag is set.\r
1911       <p>\r
1912       Note: Currently perlCode does <em>not</em>\r
1913     support the (?e=#) syntax for\r
1914       changing the escape character.\r
1915    */\r
1916 \r
1917   public static Regex perlCode(String s)\r
1918   {\r
1919     // this file is big enough, see parsePerl.java\r
1920     // for this function.\r
1921     return parsePerl.parse(s);\r
1922   }\r
1923 \r
1924   static final char back_slash = '\\';\r
1925 \r
1926   /** Checks to see if there are only literal and no special\r
1927       pattern elements in this Regex. */\r
1928   public boolean isLiteral()\r
1929   {\r
1930     Pattern x = thePattern;\r
1931     while (x != null)\r
1932     {\r
1933       if (x instanceof oneChar)\r
1934       {\r
1935         ;\r
1936       }\r
1937       else if (x instanceof Skipped)\r
1938       {\r
1939         ;\r
1940       }\r
1941       else\r
1942       {\r
1943         return false;\r
1944       }\r
1945       x = x.next;\r
1946     }\r
1947     return true;\r
1948   }\r
1949 \r
1950   /** You only need to know about this if you are inventing\r
1951       your own pattern elements. */\r
1952   public patInt countMinChars()\r
1953   {\r
1954     return thePattern.countMinChars();\r
1955   }\r
1956 \r
1957   /** You only need to know about this if you are inventing\r
1958       your own pattern elements. */\r
1959   public patInt countMaxChars()\r
1960   {\r
1961     return thePattern.countMaxChars();\r
1962   }\r
1963 \r
1964   boolean isHexDigit(StrPos sp)\r
1965   {\r
1966     boolean r =\r
1967         !sp.eos && !sp.dontMatch\r
1968         && ( (sp.c >= '0' && sp.c <= '9')\r
1969             || (sp.c >= 'a' && sp.c <= 'f')\r
1970             || (sp.c >= 'A' && sp.c <= 'F'));\r
1971     return r;\r
1972   }\r
1973 \r
1974   boolean isOctalDigit(StrPos sp, boolean first)\r
1975   {\r
1976     boolean r =\r
1977         !sp.eos && ! (first ^ sp.dontMatch)\r
1978         && sp.c >= '0' && sp.c <= '7';\r
1979     return r;\r
1980   }\r
1981 \r
1982   int getHexDigit(StrPos sp)\r
1983   {\r
1984     if (sp.c >= '0' && sp.c <= '9')\r
1985     {\r
1986       return sp.c - '0';\r
1987     }\r
1988     if (sp.c >= 'a' && sp.c <= 'f')\r
1989     {\r
1990       return sp.c - 'a' + 10;\r
1991     }\r
1992     return sp.c - 'A' + 10;\r
1993   }\r
1994 \r
1995   boolean next2Hex(StrPos sp)\r
1996   {\r
1997     StrPos sp2 = new StrPos(sp);\r
1998     sp2.inc();\r
1999     if (!isHexDigit(sp2))\r
2000     {\r
2001       return false;\r
2002     }\r
2003     sp2.inc();\r
2004     if (!isHexDigit(sp2))\r
2005     {\r
2006       return false;\r
2007     }\r
2008     return true;\r
2009   }\r
2010 \r
2011   boolean isOctalString(StrPos sp)\r
2012   {\r
2013     if (!isOctalDigit(sp, true))\r
2014     {\r
2015       return false;\r
2016     }\r
2017     StrPos sp2 = new StrPos(sp);\r
2018     sp2.inc();\r
2019     if (!isOctalDigit(sp2, false))\r
2020     {\r
2021       return false;\r
2022     }\r
2023     return true;\r
2024   }\r
2025 }\r