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