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