Class |
Line # |
Actions |
|||
---|---|---|---|---|---|
ReplaceRule | 30 | 128 | 69 |
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 | public Object clone1() |
47 | { | |
48 | 0 | return new RuleHolder(this); |
49 | } | |
50 | ||
51 | 0 | 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 | 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 | public ReplaceRule add(ReplaceRule adding) |
77 | { | |
78 | 0 | return add(this, adding); |
79 | } | |
80 | ||
81 | /** Add another ReplaceRule to the linked list. */ | |
82 | 0 | 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 | 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 | 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 | public static boolean isDefined(String s) |
322 | { | |
323 | 0 | return defs.get(s) != null; |
324 | } | |
325 | ||
326 | 0 | public static void define(String s, Regex r) |
327 | { | |
328 | 0 | defs.put(s, r); |
329 | } | |
330 | ||
331 | 0 | 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 | public static void define(String s, Transformer t) |
340 | { | |
341 | 0 | defs.put(s, t); |
342 | } | |
343 | ||
344 | 0 | 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 | public String toString1() |
355 | { | |
356 | 0 | return "${" + name + "}"; |
357 | } | |
358 | ||
359 | /** Convert to a String. */ | |
360 | 0 | 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 | public ReplaceRule arg(String s) |
384 | { | |
385 | 0 | return null; |
386 | } | |
387 | ||
388 | 0 | 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 | } |