2 // This software is now distributed according to
\r
3 // the Lesser Gnu Public License. Please see
\r
4 // http://www.gnu.org/copyleft/lesser.txt for
\r
6 // -- Happy Computing!
\r
8 package com.stevesoft.pat;
\r
12 /** ReplaceRule is a singly linked list of Objects which describe how
\r
13 to replace the matched portion of a String. The only member method
\r
14 that you absolutely need to define to use this class is apply(StringBuffer,RegRes) --
\r
15 although you may want define toString1() and clone1() (if you are
\r
16 unhappy with the default methods) that are needed by
\r
17 the clone() or toString() methods on this class.
\r
18 During the replacement process, each ReplaceRule tells the replacer
\r
19 what to add to StringBuffer and uses the contents of the Regular
\r
20 expression result to get the information it needs to
\r
21 do this. Here is an <a href="http://javaregex.com/code/fancy.java.html">example</a>
\r
23 @see com.stevesoft.pat.NullRule
\r
24 @see com.stevesoft.pat.AmpersandRule
\r
25 @see com.stevesoft.pat.BackRefRule
\r
26 @see com.stevesoft.pat.LeftRule
\r
27 @see com.stevesoft.pat.RightRule
\r
28 @see com.stevesoft.pat.StringRule
\r
30 public abstract class ReplaceRule
\r
32 /** points to the next ReplaceRule in the linked list. */
\r
33 protected ReplaceRule next = null;
\r
34 /** This function appends to the StringBufferLike the text you want
\r
35 to replaced the portion of the String last matched. */
\r
36 public abstract void apply(StringBufferLike sb, RegRes r);
\r
38 /** A rule describing how to clone only the current ReplaceRule,
\r
39 and none of the others in this linked list. It is called by
\r
40 clone() for each item in the list. */
\r
41 public Object clone1()
\r
43 return new RuleHolder(this);
\r
46 public final Object clone()
\r
48 ReplaceRule x = (ReplaceRule) clone1();
\r
49 ReplaceRule xsav = x;
\r
50 ReplaceRule y = this;
\r
51 while (y.next != null)
\r
53 x.next = (ReplaceRule) y.next.clone1();
\r
61 static ReplaceRule add(ReplaceRule head, ReplaceRule adding)
\r
65 return head = adding;
\r
67 head.addRule(adding);
\r
71 public ReplaceRule add(ReplaceRule adding)
\r
73 return add(this, adding);
\r
76 /** Add another ReplaceRule to the linked list. */
\r
77 public void addRule(ReplaceRule r)
\r
89 static Regex getvar = null;
\r
90 final static Regex getv()
\r
92 // Thanks to Michael Jimenez for pointing out the need
\r
93 // to clone getvar rather than simply returning it.
\r
94 // Previously this was not thread safe.
\r
95 //if(getvar != null) return getvar;
\r
98 return (Regex) getvar.clone();
\r
102 "(?:\\\\(\\d+)|" + // ref 1
\r
104 "(\\d+)|" + // ref 2
\r
105 "(\\w+)|" + // ref 3
\r
106 "([&'`])|" + // ref 4
\r
107 "\\{(?:(\\d+)|" + // ref 5
\r
108 "([^\n}\\\\]+))}" + // ref 6
\r
110 "\\\\([nrbtaef])|" + // ref 7
\r
111 "\\\\c([\u0000-\uFFFF])|" + // ref 8
\r
112 "\\\\x([A-Fa-f0-9]{2})|" + // ref 9
\r
113 "\\\\([\u0000-\uFFFF])" + // ref 10
\r
119 /** Compile a ReplaceRule using the text that would go between
\r
120 the second and third /'s in a typical substitution pattern
\r
121 in Perl: s/ ... / <i>The argument to ReplaceRule.perlCode</i> /.
\r
123 public static ReplaceRule perlCode(String s)
\r
125 //String sav_backGs = Regex.backGs;
\r
126 //int sav_backGto = Regex.backGto;
\r
129 int mf = 0, mt = 0;
\r
131 ReplaceRule head = null;
\r
133 while (gv.searchFrom(s, mt))
\r
135 int off = Regex.BackRefOffset - 1;
\r
136 mf = gv.matchedFrom();
\r
140 new StringRule(s.substring(mt, mf)));
\r
143 if ( (var = gv.stringMatched(1 + off)) != null
\r
144 || (var = gv.stringMatched(2 + off)) != null
\r
145 || (var = gv.stringMatched(5 + off)) != null)
\r
148 for (int i = 0; i < var.length(); i++)
\r
150 d = 8 * d + (var.charAt(i) - '0');
\r
152 if (var.length() == 1)
\r
154 head = add(head, new BackRefRule(d));
\r
158 head = new StringRule("" + (char) d);
\r
162 (var = gv.stringMatched(10 + off)) != null)
\r
164 if ("QELlUu".indexOf(var) >= 0)
\r
166 head = add(head, new CodeRule(var.charAt(0)));
\r
170 head = add(head, new StringRule(var));
\r
174 (var = gv.stringMatched(3 + off)) != null
\r
175 || (var = gv.stringMatched(4 + off)) != null
\r
176 || (var = gv.stringMatched(6 + off)) != null)
\r
180 if ( (pc = var.indexOf(':')) > 0)
\r
182 arg = var.substring(pc + 1);
\r
183 var = var.substring(0, pc);
\r
185 if (var.equals("&") || var.equals("MATCH"))
\r
187 head = add(head, new AmpersandRule());
\r
189 else if (var.equals("`") || var.equals("PREMATCH"))
\r
191 head = add(head, new LeftRule());
\r
193 else if (var.equals("'") || var.equals("POSTMATCH"))
\r
195 head = add(head, new RightRule());
\r
197 else if (var.equals("WANT_MORE_TEXT"))
\r
199 head = add(head, new WantMoreTextReplaceRule());
\r
201 else if (var.equals("POP"))
\r
203 head = add(head, new PopRule());
\r
205 else if (var.startsWith("+") && (tmp = defs.get(var.substring(1))) != null)
\r
207 if (tmp instanceof Regex)
\r
209 head = add(head, new PushRule(var.substring(1), (Regex) tmp));
\r
211 else if (tmp instanceof Transformer)
\r
213 head = add(head, new PushRule(var.substring(1), (Transformer) tmp));
\r
217 head = add(head, new StringRule("${" + var + "}"));
\r
220 else if (var.startsWith("=") && (tmp = defs.get(var.substring(1))) != null)
\r
222 if (tmp instanceof Regex)
\r
224 head = add(head, new ChangeRule(var.substring(1), (Regex) tmp));
\r
226 else if (tmp instanceof Transformer)
\r
229 new ChangeRule(var.substring(1), (Transformer) tmp));
\r
233 head = add(head, new StringRule("${" + var + "}"));
\r
236 else if ( (tmp = defs.get(var)) != null)
\r
238 if (tmp instanceof ReplaceRule)
\r
240 ReplaceRule alt = ( (ReplaceRule) tmp).arg(arg);
\r
243 alt = ( (ReplaceRule) tmp);
\r
245 head = add(head, (ReplaceRule) (alt.clone()));
\r
248 else // can't figure out how to transform this thing...
\r
250 head = add(head, new StringRule("${" + var + "}"));
\r
254 (var = gv.stringMatched(7 + off)) != null)
\r
256 char c = var.charAt(0);
\r
259 head = add(head, new StringRule("\n"));
\r
263 head = add(head, new StringRule("\t"));
\r
267 head = add(head, new StringRule("\r"));
\r
271 head = add(head, new StringRule("\r"));
\r
275 head = add(head, new StringRule("" + (char) 7));
\r
279 head = add(head, new StringRule("" + (char) 27));
\r
283 head = add(head, new StringRule("" + (char) 12));
\r
287 (var = gv.stringMatched(8 + off)) != null)
\r
289 char c = var.charAt(0);
\r
290 if (c < Ctrl.cmap.length)
\r
294 head = add(head, new StringRule("" + c));
\r
297 (var = gv.stringMatched(9 + off)) != null)
\r
300 16 * getHexDigit(var.charAt(0)) +
\r
301 getHexDigit(var.charAt(1));
\r
302 head = add(head, new StringRule("" + (char) d));
\r
304 mt = gv.matchedTo();
\r
306 if (mt <= s.length())
\r
308 head = add(head, new StringRule(s.substring(mt)));
\r
314 //Regex.backGs = sav_backGs;
\r
315 //Regex.backGto = sav_backGto;
\r
319 static Hashtable defs = new Hashtable();
\r
320 public static boolean isDefined(String s)
\r
322 return defs.get(s) != null;
\r
325 public static void define(String s, Regex r)
\r
330 public static void define(String s, ReplaceRule r)
\r
336 String name = getClass().getName();
\r
338 public static void define(String s, Transformer t)
\r
343 public static void undefine(String s)
\r
348 /** This tells how to convert just the current element (and none
\r
349 of the other items in the linked list) to a String. This
\r
350 method is called by toString() for each item in the linked
\r
352 public String toString1()
\r
354 return "${" + name + "}";
\r
357 /** Convert to a String. */
\r
358 public final String toString()
\r
360 StringBuffer sb = new StringBuffer();
\r
361 sb.append(toString1());
\r
362 ReplaceRule rr = this.next;
\r
365 sb.append(rr.toString1());
\r
368 return sb.toString();
\r
371 /** Modified the behavior of a ReplaceRule by supplying
\r
372 an argument. If a ReplaceRule named "foo" is defined
\r
373 and the pattern "s/x/${foo:5}/" is given to Regex.perlCode,
\r
374 then the "foo" the definition of "foo" will be retrieved
\r
375 and arg("5") will be called. If the result is non-null,
\r
376 that is the ReplaceRule that will be used. If the result
\r
377 is null, then the pattern works just as if it were
\r
379 @see com.stevesoft.pat.Validator#arg(java.lang.String)
\r
381 public ReplaceRule arg(String s)
\r
386 static int getHexDigit(char c)
\r
388 if (c >= '0' && c <= '9')
\r
392 if (c >= 'a' && c <= 'f')
\r
394 return c - 'a' + 10;
\r
396 return c - 'A' + 10;
\r