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
31 /** points to the next ReplaceRule in the linked list. */
\r
32 protected ReplaceRule next = null;
\r
33 /** This function appends to the StringBufferLike the text you want
\r
34 to replaced the portion of the String last matched. */
\r
35 public abstract void apply(StringBufferLike sb,RegRes r);
\r
37 /** A rule describing how to clone only the current ReplaceRule,
\r
38 and none of the others in this linked list. It is called by
\r
39 clone() for each item in the list. */
\r
40 public Object clone1() {
\r
41 return new RuleHolder(this);
\r
43 public final Object clone() {
\r
44 ReplaceRule x = (ReplaceRule)clone1();
\r
45 ReplaceRule xsav = x;
\r
46 ReplaceRule y = this;
\r
47 while(y.next != null) {
\r
48 x.next = (ReplaceRule)y.next.clone1();
\r
55 static ReplaceRule add(ReplaceRule head,ReplaceRule adding) {
\r
57 return head = adding;
\r
58 head.addRule(adding);
\r
61 public ReplaceRule add(ReplaceRule adding) {
\r
62 return add(this,adding);
\r
64 /** Add another ReplaceRule to the linked list. */
\r
65 public void addRule(ReplaceRule r) {
\r
66 if(next == null) next = r;
\r
67 else next.addRule(r);
\r
69 static Regex getvar = null;
\r
70 final static Regex getv() {
\r
71 // Thanks to Michael Jimenez for pointing out the need
\r
72 // to clone getvar rather than simply returning it.
\r
73 // Previously this was not thread safe.
\r
74 //if(getvar != null) return getvar;
\r
75 if(getvar != null) return (Regex)getvar.clone();
\r
78 "(?:\\\\(\\d+)|"+ // ref 1
\r
82 "([&'`])|"+ // ref 4
\r
83 "\\{(?:(\\d+)|"+ // ref 5
\r
84 "([^\n}\\\\]+))}"+ // ref 6
\r
86 "\\\\([nrbtaef])|"+ // ref 7
\r
87 "\\\\c([\u0000-\uFFFF])|"+ // ref 8
\r
88 "\\\\x([A-Fa-f0-9]{2})|"+ // ref 9
\r
89 "\\\\([\u0000-\uFFFF])"+ // ref 10
\r
94 /** Compile a ReplaceRule using the text that would go between
\r
95 the second and third /'s in a typical substitution pattern
\r
96 in Perl: s/ ... / <i>The argument to ReplaceRule.perlCode</i> /.
\r
98 public static ReplaceRule perlCode(String s) {
\r
99 //String sav_backGs = Regex.backGs;
\r
100 //int sav_backGto = Regex.backGto;
\r
102 int mf = 0, mt = 0;
\r
104 ReplaceRule head = null;
\r
106 while(gv.searchFrom(s,mt)) {
\r
107 int off=Regex.BackRefOffset-1;
\r
108 mf = gv.matchedFrom();
\r
111 new StringRule(s.substring(mt,mf)));
\r
113 if((var=gv.stringMatched(1+off)) != null
\r
114 || (var=gv.stringMatched(2+off)) != null
\r
115 || (var=gv.stringMatched(5+off)) != null) {
\r
117 for(int i=0;i<var.length();i++)
\r
118 d = 8*d+( var.charAt(i)-'0' );
\r
119 if(var.length() == 1)
\r
120 head=add(head,new BackRefRule(d));
\r
122 head=new StringRule(""+(char)d);
\r
124 (var=gv.stringMatched(10+off)) != null) {
\r
125 if("QELlUu".indexOf(var) >= 0)
\r
126 head=add(head,new CodeRule(var.charAt(0)) );
\r
128 head=add(head,new StringRule(var) );
\r
130 (var=gv.stringMatched(3+off)) != null
\r
131 || (var=gv.stringMatched(4+off)) != null
\r
132 || (var=gv.stringMatched(6+off)) != null) {
\r
135 if((pc=var.indexOf(':')) > 0) {
\r
136 arg = var.substring(pc+1);
\r
137 var = var.substring(0,pc);
\r
139 if(var.equals("&")||var.equals("MATCH")) {
\r
140 head=add(head,new AmpersandRule());
\r
141 } else if(var.equals("`")||var.equals("PREMATCH")) {
\r
142 head=add(head,new LeftRule());
\r
143 } else if(var.equals("'")||var.equals("POSTMATCH")) {
\r
144 head=add(head,new RightRule());
\r
145 } else if(var.equals("WANT_MORE_TEXT")) {
\r
146 head=add(head,new WantMoreTextReplaceRule());
\r
147 } else if(var.equals("POP")) {
\r
148 head=add(head,new PopRule());
\r
149 } else if(var.startsWith("+") && (tmp=defs.get(var.substring(1))) != null) {
\r
150 if(tmp instanceof Regex)
\r
151 head=add(head,new PushRule(var.substring(1),(Regex)tmp));
\r
152 else if(tmp instanceof Transformer)
\r
153 head=add(head,new PushRule(var.substring(1),(Transformer)tmp));
\r
154 else head=add(head,new StringRule("${"+var+"}"));
\r
155 } else if(var.startsWith("=") && (tmp=defs.get(var.substring(1))) != null) {
\r
156 if(tmp instanceof Regex)
\r
157 head=add(head,new ChangeRule(var.substring(1),(Regex)tmp));
\r
158 else if(tmp instanceof Transformer)
\r
159 head=add(head,new ChangeRule(var.substring(1),(Transformer)tmp));
\r
160 else head=add(head,new StringRule("${"+var+"}"));
\r
161 } else if( (tmp=defs.get(var)) != null) {
\r
162 if(tmp instanceof ReplaceRule) {
\r
163 ReplaceRule alt = ((ReplaceRule)tmp).arg(arg);
\r
164 if(alt == null) alt = ((ReplaceRule)tmp);
\r
165 head=add(head,(ReplaceRule)(alt.clone()));
\r
167 } else // can't figure out how to transform this thing...
\r
168 head=add(head,new StringRule("${"+var+"}"));
\r
170 (var = gv.stringMatched(7+off)) != null) {
\r
171 char c = var.charAt(0);
\r
173 head=add(head,new StringRule("\n"));
\r
175 head=add(head,new StringRule("\t"));
\r
177 head=add(head,new StringRule("\r"));
\r
179 head=add(head,new StringRule("\r"));
\r
181 head=add(head,new StringRule(""+(char)7));
\r
183 head=add(head,new StringRule(""+(char)27));
\r
185 head=add(head,new StringRule(""+(char)12));
\r
187 (var = gv.stringMatched(8+off)) != null) {
\r
188 char c = var.charAt(0);
\r
189 if(c < Ctrl.cmap.length)
\r
191 head=add(head,new StringRule(""+c));
\r
193 (var = gv.stringMatched(9+off)) != null) {
\r
195 16*getHexDigit(var.charAt(0))+
\r
196 getHexDigit(var.charAt(1));
\r
197 head=add(head,new StringRule(""+(char)d));
\r
199 mt = gv.matchedTo();
\r
201 if(mt <= s.length())
\r
202 head=add(head,new StringRule(s.substring(mt)));
\r
205 //Regex.backGs = sav_backGs;
\r
206 //Regex.backGto = sav_backGto;
\r
209 static Hashtable defs = new Hashtable();
\r
210 public static boolean isDefined(String s) { return defs.get(s) != null; }
\r
211 public static void define(String s,Regex r) { defs.put(s,r); }
\r
212 public static void define(String s,ReplaceRule r) {
\r
216 String name = getClass().getName();
\r
217 public static void define(String s,Transformer t) { defs.put(s,t); }
\r
218 public static void undefine(String s) { defs.remove(s); }
\r
219 /** This tells how to convert just the current element (and none
\r
220 of the other items in the linked list) to a String. This
\r
221 method is called by toString() for each item in the linked
\r
223 public String toString1() {
\r
224 return "${"+name+"}";
\r
226 /** Convert to a String. */
\r
227 public final String toString() {
\r
228 StringBuffer sb = new StringBuffer();
\r
229 sb.append(toString1());
\r
230 ReplaceRule rr = this.next;
\r
231 while(rr != null) {
\r
232 sb.append(rr.toString1());
\r
235 return sb.toString();
\r
237 /** Modified the behavior of a ReplaceRule by supplying
\r
238 an argument. If a ReplaceRule named "foo" is defined
\r
239 and the pattern "s/x/${foo:5}/" is given to Regex.perlCode,
\r
240 then the "foo" the definition of "foo" will be retrieved
\r
241 and arg("5") will be called. If the result is non-null,
\r
242 that is the ReplaceRule that will be used. If the result
\r
243 is null, then the pattern works just as if it were
\r
245 @see com.stevesoft.pat.Validator#arg(java.lang.String)
\r
247 public ReplaceRule arg(String s) { return null; }
\r
248 static int getHexDigit(char c) {
\r
249 if(c >= '0' && c <= '9')
\r
251 if(c >= 'a' && c <= 'f')
\r