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