merge from 2_4_Release branch
[jalview.git] / src / com / stevesoft / pat / Replacer.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.wrap.*;
11
12 /** Internally used class. */
13 class RegHolder
14 {
15   Regex me = null;
16
17   RegHolder prev = null;
18 }
19
20 /**
21  * Internally used class.
22  * 
23  * @see CodeRule
24  */
25 class CodeVal
26 {
27   int pos;
28
29   char code;
30
31   CodeVal(int p, char c)
32   {
33     pos = p;
34     code = c;
35   }
36
37   public String toString()
38   {
39     return "(" + pos + "," + code + ")";
40   }
41 }
42
43 /**
44  * To use this class, first use either the getReplacer() method from Transformer
45  * or Regex. You can then use replaceAll, replaceFirst, etc. methods on the
46  * Replacer in the same way that you can from either of those two classes.
47  * <p>
48  * The only potential difference between using the methods of Replacer to do the
49  * replacing is that Replacer remembers changes to the replacing object between
50  * calls to replaceAll, replaceFirst etc. For details, see the example file <a
51  * href="http://javaregex.com/code/trans3.java.html">trans3.java</a>.
52  * 
53  * @see com.stevesoft.pat.Transformer
54  * @see com.stevesoft.pat.Regex
55  */
56 public class Replacer
57 {
58   boolean first;
59
60   /** Instantiate a new Replacer. */
61   public Replacer()
62   {
63   }
64
65   public StringLike replaceFirstRegion(String s, Regex r, int start, int end)
66   {
67     return replaceFirstRegion(new StringWrap(s), r, start, end);
68   }
69
70   /**
71    * This method replaces the first occurence of the Regex in the String
72    * starting with position pos according to the Replacer rule of this object.
73    */
74   public StringLike replaceFirstRegion(StringLike s, Regex r, int start,
75           int end)
76   {
77     first = true;
78     rh.me = r;
79     rh.prev = null;
80     return dorep(s, start, end);
81   }
82
83   public StringLike replaceFirst(StringLike s)
84   {
85     return replaceFirstRegion(s, 0, s.length());
86   }
87
88   public StringLike replaceFirstFrom(StringLike s, int start)
89   {
90     return replaceFirstRegion(s, start, s.length());
91   }
92
93   public StringLike replaceFirstRegion(StringLike s, int start, int end)
94   {
95     first = true;
96     return dorep(s, start, end);
97   }
98
99   RegHolder rh = new RegHolder();
100
101   public StringLike replaceAllRegion(String s, Regex r, int start, int end)
102   {
103     return replaceAllRegion(new StringWrap(s), r, start, end);
104   }
105
106   /**
107    * This method replaces all occurences of the Regex in the String starting
108    * with postition pos according to the Replacer rule of this object.
109    */
110   public StringLike replaceAllRegion(StringLike s, Regex r, int start,
111           int end)
112   {
113     first = false;
114     // reset
115     rh.me = r;
116     rh.prev = null;
117     return dorep(s, start, end);
118   }
119
120   public StringLike replaceAll(StringLike s)
121   {
122     return replaceAllRegion(s, 0, s.length());
123   }
124
125   public StringLike replaceAllFrom(StringLike s, int start)
126   {
127     return replaceAllRegion(s, start, s.length());
128   }
129
130   public StringLike replaceAllRegion(StringLike s, int start, int end)
131   {
132     first = false;
133     return dorep(s, start, end);
134   }
135
136   public String replaceAll(String s)
137   {
138     return replaceAllRegion(new StringWrap(s), 0, s.length()).toString();
139   }
140
141   public String replaceAllFrom(String s, int start)
142   {
143     return replaceAllRegion(new StringWrap(s), start, s.length())
144             .toString();
145   }
146
147   public String replaceAllRegion(String s, int start, int end)
148   {
149     first = false;
150     return dorep(new StringWrap(s), start, end).toString();
151   }
152
153   final public boolean isSpecial(ReplaceRule x)
154   {
155     while (x != null)
156     {
157       if (x instanceof SpecialRule
158               || (x instanceof RuleHolder && ((RuleHolder) x).held instanceof SpecialRule))
159       {
160         return true;
161       }
162       x = x.next;
163     }
164     return false;
165   }
166
167   final public void apply1(RegRes rr)
168   {
169     rr.charsMatched_++;
170     apply(rr, null);
171     rr.charsMatched_--;
172   }
173
174   final StringLike dorep(StringLike s, int start, int end)
175   {
176     StringLike ret = s;
177     want_more_text = false;
178     lastMatchedTo = 0;
179     if (rh.me == null)
180     {
181       throw new NullPointerException("Replacer has null Regex pointer");
182     }
183     if (rh.me._search(s, start, end))
184     {
185       int rmn = rh.me.matchedTo();
186       if (rh.me.charsMatched() == 0 && !isSpecial(rh.me.getReplaceRule()))
187       {
188         apply1(rh.me);
189         rmn++;
190       }
191       apply(rh.me);
192       if (!first)
193       {
194         for (int i = rmn; !want_more_text && rh.me._search(s, i, end); i = rmn)
195         {
196           rmn = rh.me.matchedTo();
197           if (rh.me.charsMatched() == 0)
198           {
199             if (!isSpecial(rh.me.getReplaceRule()))
200             {
201               apply1(rh.me);
202             }
203             rmn++;
204           }
205           apply(rh.me);
206         }
207       }
208       ret = finish();
209       ret = ret == null ? s : ret;
210     }
211     return ret;
212   }
213
214   StringBufferLike sb = null;
215
216   StringLike src = null;
217
218   int pos = 0;
219
220   /**
221    * This method allows you to apply the results of several matches in a
222    * sequence to modify a String of text. Each call in the sequence must operate
223    * on the same piece of text and the matchedFrom() of each RegRes given to
224    * this method must be greater in value than the preceeding RegRes's
225    * matchedTo() value.
226    */
227   public void apply(RegRes r, ReplaceRule rp)
228   {
229     if (rp == null || (rp.next == null && rp instanceof AmpersandRule))
230     {
231       return;
232     }
233     if (r.didMatch())
234     {
235       if (src == null)
236       {
237         src = r.getStringLike();
238       }
239       if (sb == null)
240       {
241         sb = new StringBufferLike(src.newStringBufferLike());
242       }
243       int rmf = r.matchedFrom();
244       for (int ii = pos; ii < rmf; ii++)
245       {
246         sb.append(src.charAt(ii));
247       }
248
249       for (ReplaceRule x = rp; x != null; x = x.next)
250       {
251         x.apply(sb, r);
252         if (x instanceof SpecialRule)
253         {
254           if (x instanceof WantMoreTextReplaceRule && want_more_text_enable)
255           {
256             want_more_text = true;
257           }
258           else if (x instanceof PushRule)
259           {
260             RegHolder rh2 = new RegHolder();
261             rh2.me = ((PushRule) x).NewRule;
262             rh2.prev = rh;
263             rh = rh2;
264           }
265           else if (x instanceof PopRule)
266           {
267             if (rh.prev != null)
268             {
269               rh = rh.prev;
270             }
271           }
272           else if (x instanceof ChangeRule)
273           {
274             rh.me = ((ChangeRule) x).NewRule;
275           }
276         }
277       }
278       if (!want_more_text)
279       {
280         pos = r.matchedTo();
281       }
282     }
283   }
284
285   boolean want_more_text = false, want_more_text_enable = false;
286
287   public boolean WantMoreText()
288   {
289     return want_more_text;
290   }
291
292   /**
293    * Another form of apply, it is the same as apply(r,r.getReplaceRule()).
294    */
295   public void apply(Regex r)
296   {
297     apply(r, r.getReplaceRule());
298   }
299
300   /**
301    * This finishes the replacement, appending the right() part of the last
302    * RegRes given to substitute(RegRes). After this method is called, the
303    * Replace object is reset to perform another substitution. If no RegRes
304    * objects with a true didMatch are applied, this returns null.
305    */
306   public StringLike finish()
307   {
308     if (src == null)
309     {
310       return null;
311     }
312     // sb.append(src.substring(pos,src.length()));
313     int s_end = src.length();
314     for (int ii = pos; ii < s_end; ii++)
315     {
316       sb.append(src.charAt(ii));
317     }
318     src = null;
319     lastMatchedTo = pos;
320     pos = 0;
321     StringLike retstr = sb.toStringLike();
322     sb = null;
323     return retstr;
324   }
325
326   int lastMatchedTo = 0;
327
328   public Object clone()
329   {
330     Replacer r = new Replacer();
331     r.first = first;
332     r.src = src;
333     r.sb = sb;
334     r.pos = pos;
335     r.lastMatchedTo = lastMatchedTo;
336     r.want_more_text = want_more_text;
337     r.want_more_text_enable = want_more_text_enable;
338     r.rh.me = rh.me;
339     r.rh.prev = rh.prev;
340     return r;
341   }
342
343   public int lastMatchedTo()
344   {
345     return lastMatchedTo;
346   }
347
348   public Regex getRegex()
349   {
350     return rh.me;
351   }
352
353   public void setSource(StringLike sl)
354   {
355     src = sl;
356   }
357
358   public void setBuffer(StringBufferLike sbl)
359   {
360     sb = sbl;
361   }
362
363   public void setPos(int pos)
364   {
365     this.pos = pos;
366   }
367 }