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