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