Clover icon

Coverage Report

  1. Project Clover database Fri Dec 6 2024 13:47:14 GMT
  2. Package com.stevesoft.pat

File ReplaceRule.java

 

Coverage histogram

../../../img/srcFileCovDistChart1.png
56% of files have more coverage

Code metrics

84
128
16
1
400
312
69
0.54
8
16
4.31

Classes

Class Line # Actions
ReplaceRule 30 128 69
0.1052631610.5%
 

Contributing tests

This file is covered by 28 tests. .

Source view

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.util.Hashtable;
11   
12    /**
13    * ReplaceRule is a singly linked list of Objects which describe how to replace
14    * the matched portion of a String. The only member method that you absolutely
15    * need to define to use this class is apply(StringBuffer,RegRes) -- although
16    * you may want define toString1() and clone1() (if you are unhappy with the
17    * default methods) that are needed by the clone() or toString() methods on this
18    * class. During the replacement process, each ReplaceRule tells the replacer
19    * what to add to StringBuffer and uses the contents of the Regular expression
20    * result to get the information it needs to do this. Here is an
21    * <a href="http://javaregex.com/code/fancy.java.html">example</a>
22    *
23    * @see com.stevesoft.pat.NullRule
24    * @see com.stevesoft.pat.AmpersandRule
25    * @see com.stevesoft.pat.BackRefRule
26    * @see com.stevesoft.pat.LeftRule
27    * @see com.stevesoft.pat.RightRule
28    * @see com.stevesoft.pat.StringRule
29    */
 
30    public abstract class ReplaceRule
31    {
32    /** points to the next ReplaceRule in the linked list. */
33    protected ReplaceRule next = null;
34   
35    /**
36    * This function appends to the StringBufferLike the text you want to replaced
37    * the portion of the String last matched.
38    */
39    public abstract void apply(StringBufferLike sb, RegRes r);
40   
41    /**
42    * A rule describing how to clone only the current ReplaceRule, and none of
43    * the others in this linked list. It is called by clone() for each item in
44    * the list.
45    */
 
46  0 toggle public Object clone1()
47    {
48  0 return new RuleHolder(this);
49    }
50   
 
51  0 toggle public final Object clone()
52    {
53  0 ReplaceRule x = (ReplaceRule) clone1();
54  0 ReplaceRule xsav = x;
55  0 ReplaceRule y = this;
56  0 while (y.next != null)
57    {
58  0 x.next = (ReplaceRule) y.next.clone1();
59  0 x.name = y.name;
60  0 x = x.next;
61  0 y = y.next;
62    }
63  0 return xsav;
64    }
65   
 
66  199 toggle static ReplaceRule add(ReplaceRule head, ReplaceRule adding)
67    {
68  199 if (head == null)
69    {
70  199 return head = adding;
71    }
72  0 head.addRule(adding);
73  0 return head;
74    }
75   
 
76  0 toggle public ReplaceRule add(ReplaceRule adding)
77    {
78  0 return add(this, adding);
79    }
80   
81    /** Add another ReplaceRule to the linked list. */
 
82  0 toggle public void addRule(ReplaceRule r)
83    {
84  0 if (next == null)
85    {
86  0 next = r;
87    }
88    else
89    {
90  0 next.addRule(r);
91    }
92    }
93   
94    static Regex getvar = null;
95   
 
96  199 toggle final static Regex getv()
97    {
98    // Thanks to Michael Jimenez for pointing out the need
99    // to clone getvar rather than simply returning it.
100    // Previously this was not thread safe.
101    // if(getvar != null) return getvar;
102  199 if (getvar != null)
103    {
104  196 return (Regex) getvar.clone();
105    }
106  3 getvar = new Regex("(?:\\\\(\\d+)|" + // ref 1
107    "\\$(?:" + "(\\d+)|" + // ref 2
108    "(\\w+)|" + // ref 3
109    "([&'`])|" + // ref 4
110    "\\{(?:(\\d+)|" + // ref 5
111    "([^\n}\\\\]+))}" + // ref 6
112    ")|" + "\\\\([nrbtaef])|" + // ref 7
113    "\\\\c([\u0000-\uFFFF])|" + // ref 8
114    "\\\\x([A-Fa-f0-9]{2})|" + // ref 9
115    "\\\\([\u0000-\uFFFF])" + // ref 10
116    ")");
117  3 getvar.optimize();
118  3 return getvar;
119    }
120   
121    /**
122    * Compile a ReplaceRule using the text that would go between the second and
123    * third /'s in a typical substitution pattern in Perl: s/ ... / <i>The
124    * argument to ReplaceRule.perlCode</i> /.
125    */
 
126  199 toggle public static ReplaceRule perlCode(String s)
127    {
128    // String sav_backGs = Regex.backGs;
129    // int sav_backGto = Regex.backGto;
130  199 try
131    {
132  199 int mf = 0, mt = 0;
133  199 Regex gv = getv();
134  199 ReplaceRule head = null;
135  199 Object tmp = null;
136  199 while (gv.searchFrom(s, mt))
137    {
138  0 int off = Regex.BackRefOffset - 1;
139  0 mf = gv.matchedFrom();
140  0 if (mf > mt)
141    {
142  0 head = add(head, new StringRule(s.substring(mt, mf)));
143    }
144  0 String var = null;
145  0 if ((var = gv.stringMatched(1 + off)) != null
146    || (var = gv.stringMatched(2 + off)) != null
147    || (var = gv.stringMatched(5 + off)) != null)
148    {
149  0 int d = 0;
150  0 for (int i = 0; i < var.length(); i++)
151    {
152  0 d = 8 * d + (var.charAt(i) - '0');
153    }
154  0 if (var.length() == 1)
155    {
156  0 head = add(head, new BackRefRule(d));
157    }
158    else
159    {
160  0 head = new StringRule("" + (char) d);
161    }
162    }
163  0 else if ((var = gv.stringMatched(10 + off)) != null)
164    {
165  0 if ("QELlUu".indexOf(var) >= 0)
166    {
167  0 head = add(head, new CodeRule(var.charAt(0)));
168    }
169    else
170    {
171  0 head = add(head, new StringRule(var));
172    }
173    }
174  0 else if ((var = gv.stringMatched(3 + off)) != null
175    || (var = gv.stringMatched(4 + off)) != null
176    || (var = gv.stringMatched(6 + off)) != null)
177    {
178  0 String arg = "";
179  0 int pc;
180  0 if ((pc = var.indexOf(':')) > 0)
181    {
182  0 arg = var.substring(pc + 1);
183  0 var = var.substring(0, pc);
184    }
185  0 if (var.equals("&") || var.equals("MATCH"))
186    {
187  0 head = add(head, new AmpersandRule());
188    }
189  0 else if (var.equals("`") || var.equals("PREMATCH"))
190    {
191  0 head = add(head, new LeftRule());
192    }
193  0 else if (var.equals("'") || var.equals("POSTMATCH"))
194    {
195  0 head = add(head, new RightRule());
196    }
197  0 else if (var.equals("WANT_MORE_TEXT"))
198    {
199  0 head = add(head, new WantMoreTextReplaceRule());
200    }
201  0 else if (var.equals("POP"))
202    {
203  0 head = add(head, new PopRule());
204    }
205  0 else if (var.startsWith("+")
206    && (tmp = defs.get(var.substring(1))) != null)
207    {
208  0 if (tmp instanceof Regex)
209    {
210  0 head = add(head, new PushRule(var.substring(1), (Regex) tmp));
211    }
212  0 else if (tmp instanceof Transformer)
213    {
214  0 head = add(head,
215    new PushRule(var.substring(1), (Transformer) tmp));
216    }
217    else
218    {
219  0 head = add(head, new StringRule("${" + var + "}"));
220    }
221    }
222  0 else if (var.startsWith("=")
223    && (tmp = defs.get(var.substring(1))) != null)
224    {
225  0 if (tmp instanceof Regex)
226    {
227  0 head = add(head,
228    new ChangeRule(var.substring(1), (Regex) tmp));
229    }
230  0 else if (tmp instanceof Transformer)
231    {
232  0 head = add(head,
233    new ChangeRule(var.substring(1), (Transformer) tmp));
234    }
235    else
236    {
237  0 head = add(head, new StringRule("${" + var + "}"));
238    }
239    }
240  0 else if ((tmp = defs.get(var)) != null)
241    {
242  0 if (tmp instanceof ReplaceRule)
243    {
244  0 ReplaceRule alt = ((ReplaceRule) tmp).arg(arg);
245  0 if (alt == null)
246    {
247  0 alt = ((ReplaceRule) tmp);
248    }
249  0 head = add(head, (ReplaceRule) (alt.clone()));
250    }
251    }
252    else
253    // can't figure out how to transform this thing...
254    {
255  0 head = add(head, new StringRule("${" + var + "}"));
256    }
257    }
258  0 else if ((var = gv.stringMatched(7 + off)) != null)
259    {
260  0 char c = var.charAt(0);
261  0 if (c == 'n')
262    {
263  0 head = add(head, new StringRule("\n"));
264    }
265  0 else if (c == 't')
266    {
267  0 head = add(head, new StringRule("\t"));
268    }
269  0 else if (c == 'r')
270    {
271  0 head = add(head, new StringRule("\r"));
272    }
273  0 else if (c == 'b')
274    {
275  0 head = add(head, new StringRule("\r"));
276    }
277  0 else if (c == 'a')
278    {
279  0 head = add(head, new StringRule("" + (char) 7));
280    }
281  0 else if (c == 'e')
282    {
283  0 head = add(head, new StringRule("" + (char) 27));
284    }
285  0 else if (c == 'f')
286    {
287  0 head = add(head, new StringRule("" + (char) 12));
288    }
289    }
290  0 else if ((var = gv.stringMatched(8 + off)) != null)
291    {
292  0 char c = var.charAt(0);
293  0 if (c < Ctrl.cmap.length)
294    {
295  0 c = Ctrl.cmap[c];
296    }
297  0 head = add(head, new StringRule("" + c));
298    }
299  0 else if ((var = gv.stringMatched(9 + off)) != null)
300    {
301  0 int d = 16 * getHexDigit(var.charAt(0))
302    + getHexDigit(var.charAt(1));
303  0 head = add(head, new StringRule("" + (char) d));
304    }
305  0 mt = gv.matchedTo();
306    }
307  199 if (mt <= s.length())
308    {
309  199 head = add(head, new StringRule(s.substring(mt)));
310    }
311  199 return head;
312    } finally
313    {
314    // Regex.backGs = sav_backGs;
315    // Regex.backGto = sav_backGto;
316    }
317    }
318   
319    static Hashtable defs = new Hashtable();
320   
 
321  0 toggle public static boolean isDefined(String s)
322    {
323  0 return defs.get(s) != null;
324    }
325   
 
326  0 toggle public static void define(String s, Regex r)
327    {
328  0 defs.put(s, r);
329    }
330   
 
331  0 toggle public static void define(String s, ReplaceRule r)
332    {
333  0 defs.put(s, r);
334  0 r.name = s;
335    }
336   
337    String name = getClass().getName();
338   
 
339  0 toggle public static void define(String s, Transformer t)
340    {
341  0 defs.put(s, t);
342    }
343   
 
344  0 toggle public static void undefine(String s)
345    {
346  0 defs.remove(s);
347    }
348   
349    /**
350    * This tells how to convert just the current element (and none of the other
351    * items in the linked list) to a String. This method is called by toString()
352    * for each item in the linked list.
353    */
 
354  0 toggle public String toString1()
355    {
356  0 return "${" + name + "}";
357    }
358   
359    /** Convert to a String. */
 
360  0 toggle public final String toString()
361    {
362  0 StringBuffer sb = new StringBuffer();
363  0 sb.append(toString1());
364  0 ReplaceRule rr = this.next;
365  0 while (rr != null)
366    {
367  0 sb.append(rr.toString1());
368  0 rr = rr.next;
369    }
370  0 return sb.toString();
371    }
372   
373    /**
374    * Modified the behavior of a ReplaceRule by supplying an argument. If a
375    * ReplaceRule named "foo" is defined and the pattern "s/x/${foo:5}/" is given
376    * to Regex.perlCode, then the "foo" the definition of "foo" will be retrieved
377    * and arg("5") will be called. If the result is non-null, that is the
378    * ReplaceRule that will be used. If the result is null, then the pattern
379    * works just as if it were "s/x/${foo}/".
380    *
381    * @see com.stevesoft.pat.Validator#arg(java.lang.String)
382    */
 
383  0 toggle public ReplaceRule arg(String s)
384    {
385  0 return null;
386    }
387   
 
388  0 toggle static int getHexDigit(char c)
389    {
390  0 if (c >= '0' && c <= '9')
391    {
392  0 return c - '0';
393    }
394  0 if (c >= 'a' && c <= 'f')
395    {
396  0 return c - 'a' + 10;
397    }
398  0 return c - 'A' + 10;
399    }
400    }