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