merge from 2_4_Release branch
[jalview.git] / src / com / stevesoft / pat / RegexWriter.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.io.*;
11
12 import com.stevesoft.pat.wrap.*;
13
14 /**
15  * A basic extension of FilterWriter that uses Transformer to make replacements
16  * in data as it is written out. It attempts to transform a string whenever the
17  * End-of-Line (EOL) character is written (which is, by default, the carriage
18  * return '\n'). Only the transformed portion of the line is written out,
19  * allowing the RegexWriter to wait until a complete pattern is present before
20  * attempting to write out info. Until a pattern completes, data is stored in a
21  * StringBuffer -- which can be accessed through the length() and charAt()
22  * methods of this class.
23  * <p>
24  * Note a subtlety here -- while a Transformer normally matches at higher
25  * priority against the pattern added to it first, this will not necessarily be
26  * true when a multi-line match is in progress because one of the complete
27  * multi-line patterns may not be completely loaded in RegexWriter's buffer. For
28  * this reason, the Transformer class is equipped with a way to add a pattern
29  * and replacement rule in three pieces -- a beginning (once this matches,
30  * nothing else in the Transformer can match until the whole pattern matches),
31  * an ending (the whole pattern is a String formed by adding the beginning and
32  * ending), and a ReplaceRule.
33  * <p>
34  * An illustration of this is given in the this <a
35  * href="../test/trans.java">example.</a>
36  */
37 public class RegexWriter extends Writer
38 {
39   Replacer repr;
40
41   Writer w;
42
43   WriterWrap ww;
44
45   StringBuffer sb = new StringBuffer();
46
47   PartialBuffer wrap = new PartialBuffer(sb);
48
49   int pos, epos;
50
51   int interval = 128;
52
53   int bufferSize = 2 * 1024;
54
55   public RegexWriter(Transformer t, Writer w)
56   {
57     this.w = w;
58     ww = new WriterWrap(w);
59     repr = t.getReplacer();
60     repr.setBuffer(new StringBufferLike(ww));
61     repr.setSource(wrap);
62   }
63
64   public RegexWriter(Regex r, Writer w)
65   {
66     this.w = w;
67     ww = new WriterWrap(w);
68     repr = r.getReplacer();
69     repr.setBuffer(new StringBufferLike(ww));
70     repr.setSource(wrap);
71   }
72
73   char EOLchar = '\n';
74
75   /**
76    * This method no longer serves any purpose.
77    * 
78    * @deprecated
79    */
80   public char getEOLchar()
81   {
82     return EOLchar;
83   }
84
85   /**
86    * This method no longer serves any purpose.
87    * 
88    * @deprecated
89    */
90   public void setEOLchar(char c)
91   {
92     EOLchar = c;
93   }
94
95   int max_lines = 2;
96
97   /**
98    * This method no longer serves any purpose.
99    * 
100    * @deprecated
101    */
102   public int getMaxLines()
103   {
104     return max_lines;
105   }
106
107   /**
108    * This method no longer serves any purpose.
109    * 
110    * @deprecated
111    */
112   public void setMaxLines(int ml)
113   {
114     max_lines = ml;
115   }
116
117   void write() throws IOException
118   {
119     Regex rex = repr.getRegex();
120     int eposOld = epos;
121     if (rex.matchAt(wrap, epos) && !wrap.overRun)
122     {
123       while (pos < epos)
124       {
125         w.write(sb.charAt(pos++));
126       }
127       int to = rex.matchedTo();
128       repr.setPos(to);
129       repr.apply(rex, rex.getReplaceRule());
130       epos = pos = to;
131       if (epos == eposOld && epos < sb.length())
132       {
133         epos++;
134       }
135     }
136     else if (!wrap.overRun && epos < sb.length())
137     {
138       epos++;
139     }
140     while (pos < epos)
141     {
142       w.write(sb.charAt(pos++));
143     }
144     if (epos == sb.length())
145     {
146       sb.setLength(1);
147       pos = epos = 1;
148     }
149     else if (pos > bufferSize)
150     {
151       for (int i = bufferSize; i < sb.length(); i++)
152       {
153         sb.setCharAt(i - bufferSize, sb.charAt(i));
154       }
155       pos -= bufferSize;
156       epos -= bufferSize;
157       sb.setLength(sb.length() - bufferSize);
158     }
159   }
160
161   public void write(char[] ca, int b, int n) throws IOException
162   {
163     int m = b + n;
164     for (int i = b; i < m; i++)
165     {
166       sb.append(ca[i]);
167       if (sb.length() % interval == interval - 1)
168       {
169         wrap.overRun = false;
170         while (epos + interval < sb.length() && !wrap.overRun)
171         {
172           write();
173         }
174       }
175     }
176   }
177
178   public void flush() throws IOException
179   {
180   }
181
182   public void close() throws IOException
183   {
184     wrap.allowOverRun = false;
185     wrap.overRun = false;
186     while (epos < sb.length())
187     {
188       write();
189     }
190     write();
191     w.close();
192   }
193
194   /** The current size of the StringBuffer in use by RegexWriter. */
195   public int length()
196   {
197     return sb.length();
198   }
199
200   /** The character at location i in the StringBuffer. */
201   public char charAt(int i)
202   {
203     return sb.charAt(i);
204   }
205
206   /** Set the interval at which regex patterns are checked. */
207   public void setInterval(int i)
208   {
209     interval = i;
210   }
211
212   /** Get the interval at which regex matches are checked. */
213   public int getInterval()
214   {
215     return interval;
216   }
217
218   /** Get the buffer size. */
219   public int getBufferSize()
220   {
221     return bufferSize;
222   }
223
224   /** Set the buffer size. */
225   public void setBufferSize(int i)
226   {
227     bufferSize = i;
228   }
229
230   static void test(String re, String inp, int n) throws Exception
231   {
232     StringWriter sw = new StringWriter();
233     Regex rex = Regex.perlCode(re);
234     String res1 = rex.replaceAll(inp);
235     RegexWriter rw = new RegexWriter(rex, sw);
236     for (int i = 0; i < inp.length(); i++)
237     {
238       rw.write(inp.charAt(i));
239     }
240     rw.close();
241     String res2 = sw.toString();
242     if (!res1.equals(res2))
243     {
244       System.out.println("nmax=" + n);
245       System.out.println("re=" + re);
246       System.out.println("inp=" + inp);
247       System.out.println("res1=" + res1);
248       System.out.println("res2=" + res2);
249       System.exit(255);
250     }
251   }
252
253   public static void main(String[] args) throws Exception
254   {
255     for (int n = 1; n <= 1; n++)
256     {
257       test("s/x/y/", "-----x123456789", n);
258       test("s/x/y/", "x123456789", n);
259       test("s/x/y/", "-----x", n);
260       test("s/x.*?x/y/", ".xx..x..x...x...x....x....x", n);
261       test("s/x.*x/[$&]/", "--x........x--xx", n);
262       test("s/x.*x/[$&]/", "--x........x------", n);
263       test("s/.$/a/m", "bb\nbbb\nbbbb\nbbbbb\nbbbbbb\nbbbbbbbbbbbb", n);
264       test("s/.$/a/", "123", n);
265       test("s/.$/a/", "bb\nbbb\nbbbb\nbbbbb\nbbbbbb\nbb", n);
266       test("s/^./a/", "bb\nbbb\nbbbb\nbbbbb\nbbbbbb\nbb", n);
267       test("s/$/a/", "bbb", n);
268       test("s/^/a/", "bbb", n);
269       test("s/^/a/", "", n);
270       test("s{.*}{N}", "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", n);
271       test("s/.{0,7}/y/", "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", n);
272       test("s/x/$&/", "xxx", n);
273     }
274     System.out.println("Success!!!");
275   }
276 }