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