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