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