merge from 2_4_Release branch
[jalview.git] / src / com / stevesoft / pat / Pattern.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 java.util.*;
11
12 /**
13  Shareware: package pat
14  <a href="copyright.html">Copyright 2001, Steven R. Brandt</a>
15  */
16 /**
17  * Class Pattern is the base class on which all the other pattern elements are
18  * built.
19  */
20
21 public abstract class Pattern
22 {
23   /**
24    * The ESC character, the user can provide his own value for the escape
25    * character through regex.esc
26    */
27   public final static char ESC = '\\';
28
29   final static String PROTECT_THESE = "[]{}(),$,-\"^.";
30
31   /**
32    * The interal match function, it must be provided by any class which wishes
33    * to extend Pattern.
34    */
35   public abstract int matchInternal(int i, Pthings p);
36
37   public abstract String toString();
38
39   // Class Pattern is a singly linked list
40   // chained together by member next. The member
41   // parent is used so that sub patterns can access
42   // the chain they are branching from.
43   Pattern next = null, parent = null;
44
45   /**
46    * This gets the next element of a Pattern that we wish to match. If we are at
47    * the end of a subchain of patterns, it will return us to the parent chain.
48    */
49   public Pattern getNext()
50   {
51     return next != null ? next : (parent == null ? null : parent.getNext());
52   }
53
54   /**
55    * Call this method if you have a pattern element that takes a sub pattern
56    * (such as Or), and after you have added a sub pattern to the current pattern
57    * element.
58    */
59   public void setParent(Pattern p)
60   {
61     if (next != null)
62     {
63       next.setParent(p);
64     }
65     else
66     {
67       parent = p;
68     }
69   }
70
71   /**
72    * This determines if the remainder of a Pattern matches. Type "return
73    * nextMatch" from within matchInternal if the current Pattern matches.
74    * Otherwise, return a -1.
75    */
76   public int nextMatch(int i, Pthings pt)
77   {
78     Pattern p = getNext();
79     /*
80      * if(p == null) return i; return p.matchInternal(i,pt);
81      */
82     return p == null ? i : p.matchInternal(i, pt);
83   }
84
85   /**
86    * This is a toString() for the remainder of the Pattern elements after this
87    * one. use this when overriding toString(). Called from within toString().
88    */
89   public String nextString()
90   {
91     if (next == null)
92     {
93       return "";
94     }
95     return next.toString();
96   }
97
98   /** a method to detect whether char c is in String s */
99   final static boolean inString(char c, String s)
100   {
101     int i;
102     for (i = 0; i < s.length(); i++)
103     {
104       if (s.charAt(i) == c)
105       {
106         return true;
107       }
108     }
109     return false;
110   }
111
112   /**
113    * A method to create a string that protects the characters listed in
114    * PROTECT_THESE by prepending the esc character. The esc character itself is
115    * automatically protected.
116    */
117   final static String protect(String s, String PROTECT_THESE, char esc)
118   {
119     int i;
120     StringBuffer sb = new StringBuffer();
121     String p = PROTECT_THESE + esc;
122     for (i = 0; i < s.length(); i++)
123     {
124       char c = s.charAt(i);
125       if (inString(c, p))
126       {
127         sb.append(esc);
128       }
129       sb.append(c);
130     }
131     return sb.toString();
132   }
133
134   /**
135    * This can be used to perform a match test from within class Pattern.
136    */
137   public int match(StringLike s, Pthings pt)
138   {
139     return matchAt(s, 0, pt);
140   }
141
142   /**
143    * This can be used to perform a match test from within class Pattern.
144    */
145   public int matchAt(StringLike s, int i, Pthings pt)
146   {
147     pt.src = s;
148     int r = matchInternal(i, pt);
149     if (r < 0)
150     {
151       return -1;
152     }
153     mfrom = r < i ? r + 1 : i;
154     return r < i ? i - r - 1 : r - i;
155   }
156
157   int mfrom = 0;
158
159   // Detect masked characters
160   final boolean Masked(int i, Pthings pt)
161   {
162     return pt.cbits == null ? false : pt.cbits.get(i);
163   }
164
165   /** add a Pattern to the singly-linked Pattern chain. */
166   public Pattern add(Pattern p)
167   {
168     if (next == null)
169     {
170       if (p == null)
171       {
172         return this;
173       }
174       next = p;
175       p.parent = parent;
176       parent = null;
177     }
178     else
179     {
180       next.add(p);
181     }
182     return this;
183   }
184
185   /**
186    * The minimum number of characters which this pattern element can match.
187    */
188   public patInt minChars()
189   {
190     return new patInt(0);
191   }
192
193   /**
194    * The maximum number of characters which this pattern element can match.
195    */
196   public patInt maxChars()
197   {
198     return new patInf();
199   }
200
201   /** return minimum number of characters in pattern */
202   public final patInt countMinChars()
203   {
204     Pattern p = this;
205     patInt sum = new patInt(0);
206     while (p != null)
207     {
208       sum.pluseq(p.minChars());
209       p = p.next;
210     }
211     return sum;
212   }
213
214   /** return maximum number of characters in pattern */
215   public final patInt countMaxChars()
216   {
217     Pattern p = this;
218     patInt sum = new patInt(0);
219     while (p != null)
220     {
221       sum.pluseq(p.maxChars());
222       p = p.next;
223     }
224     return sum;
225   }
226
227   // This method is only needed by Multi_stage2 so far...
228   // the reason is that it may try something else after a
229   // match succeeds. OrMark will only record the last thing
230   // tried in marks, so we need to backup the result of the
231   // last successful match and restore it if the next one
232   // does not succeed.
233   final int testMatch(Pattern p, int pos, Pthings pt)
234   {
235     int[] tab = null;
236     if (pt.marks != null)
237     {
238       try
239       {
240         tab = new int[pt.marks.length];
241         for (int i = 0; i < tab.length; i++)
242         {
243           tab[i] = pt.marks[i];
244         }
245       } catch (Throwable t)
246       {
247       }
248     }
249     int ret = p.matchInternal(pos, pt);
250     if (ret < 0)
251     {
252       pt.marks = tab;
253     }
254     return ret;
255   }
256
257   /**
258    * Clones this pattern elements without cloning others in the linked list.
259    */
260   Pattern clone1(Hashtable h)
261   {
262     throw new Error("No such method as clone1 for " + getClass().getName());
263   }
264
265   Pattern clone(Hashtable h)
266   {
267     Pattern p = (Pattern) h.get(this);
268     if (p != null)
269     {
270       return p;
271     }
272     p = clone1(h);
273     if (p == null)
274     {
275       throw new Error("Null from clone1!");
276     }
277     h.put(this, p);
278     h.put(p, p);
279     if (next != null)
280     {
281       p.next = next.clone(h);
282     }
283     if (parent != null)
284     {
285       p.parent = parent.clone(h);
286     }
287     return p;
288   }
289
290   public boolean equals(Object o)
291   {
292     return o == this;
293   }
294 };