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