needed for applet search
[jalview.git] / src / com / stevesoft / pat / ReplaceRule.java
1 //\r
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
5 // the details.\r
6 //    -- Happy Computing!\r
7 //\r
8 package com.stevesoft.pat;\r
9 \r
10 import java.util.*;\r
11 \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
22 \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
29     */\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
36 \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
42     }\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
49             x.name = y.name;\r
50             x = x.next;\r
51             y = y.next;\r
52         }\r
53         return xsav;\r
54     }\r
55     static ReplaceRule add(ReplaceRule head,ReplaceRule adding) {\r
56         if(head == null)\r
57             return head = adding;\r
58         head.addRule(adding);\r
59         return head;\r
60     }\r
61     public ReplaceRule add(ReplaceRule adding) {\r
62         return add(this,adding);\r
63     }\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
68     }\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
76         getvar=\r
77             new Regex(\r
78             "(?:\\\\(\\d+)|"+ // ref 1\r
79             "\\$(?:"+\r
80               "(\\d+)|"+ // ref 2\r
81               "(\\w+)|"+ // ref 3\r
82               "([&'`])|"+ // ref 4\r
83               "\\{(?:(\\d+)|"+ // ref 5\r
84                 "([^\n}\\\\]+))}"+ // ref 6\r
85               ")|"+ \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
90             ")");\r
91         getvar.optimize();\r
92         return getvar;\r
93     }\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
97         */\r
98     public static ReplaceRule perlCode(String s) {\r
99         //String sav_backGs = Regex.backGs;\r
100         //int sav_backGto = Regex.backGto;\r
101         try {\r
102             int mf = 0, mt = 0;\r
103             Regex gv = getv();\r
104             ReplaceRule head = null;\r
105             Object tmp = null;\r
106             while(gv.searchFrom(s,mt)) {\r
107                 int off=Regex.BackRefOffset-1;\r
108                 mf = gv.matchedFrom();\r
109                 if(mf > mt)\r
110                     head=add(head,\r
111                         new StringRule(s.substring(mt,mf)));\r
112                 String var = null;\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
116                     int d=0;\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
121                     else\r
122                       head=new StringRule(""+(char)d);\r
123                 } else if(\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
127                     else\r
128                         head=add(head,new StringRule(var) );\r
129                 } else if(\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
133                     String arg = "";\r
134                     int pc;\r
135                     if((pc=var.indexOf(':')) > 0) {\r
136                         arg = var.substring(pc+1);\r
137                         var = var.substring(0,pc);\r
138                     }\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
166                         }\r
167                     } else // can't figure out how to transform this thing...\r
168                         head=add(head,new StringRule("${"+var+"}"));\r
169                 } else if(\r
170                   (var = gv.stringMatched(7+off)) != null) {\r
171                   char c = var.charAt(0);\r
172                   if(c == 'n')\r
173                     head=add(head,new StringRule("\n"));\r
174                   else if(c == 't')\r
175                     head=add(head,new StringRule("\t"));\r
176                   else if(c == 'r')\r
177                     head=add(head,new StringRule("\r"));\r
178                   else if(c == 'b')\r
179                     head=add(head,new StringRule("\r"));\r
180                   else if(c == 'a')\r
181                     head=add(head,new StringRule(""+(char)7));\r
182                   else if(c == 'e')\r
183                     head=add(head,new StringRule(""+(char)27));\r
184                   else if(c == 'f')\r
185                     head=add(head,new StringRule(""+(char)12));\r
186                 } else if(\r
187                   (var = gv.stringMatched(8+off)) != null) {\r
188                   char c = var.charAt(0);\r
189                   if(c < Ctrl.cmap.length)\r
190                     c = Ctrl.cmap[c];\r
191                   head=add(head,new StringRule(""+c));\r
192                 } else if(\r
193                   (var = gv.stringMatched(9+off)) != null) {\r
194                   int d =\r
195                     16*getHexDigit(var.charAt(0))+\r
196                     getHexDigit(var.charAt(1));\r
197                   head=add(head,new StringRule(""+(char)d));\r
198                 }\r
199                 mt = gv.matchedTo();\r
200             }\r
201             if(mt <= s.length())\r
202                 head=add(head,new StringRule(s.substring(mt)));\r
203             return head;\r
204         } finally {\r
205             //Regex.backGs = sav_backGs;\r
206             //Regex.backGto = sav_backGto;\r
207         }\r
208     }\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
213         defs.put(s,r);\r
214         r.name = s;\r
215     }\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
222         list. */\r
223     public String toString1() {\r
224         return "${"+name+"}";\r
225     }\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
233             rr = rr.next;\r
234         }\r
235         return sb.toString();\r
236     }\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
244         "s/x/${foo}/".\r
245         @see com.stevesoft.pat.Validator#arg(java.lang.String)\r
246         */\r
247     public ReplaceRule arg(String s) { return null; }\r
248     static int getHexDigit(char c) {\r
249       if(c >= '0' && c <= '9')\r
250         return c - '0';\r
251       if(c >= 'a' && c <= 'f')\r
252         return c - 'a'+10;\r
253       return c - 'A'+10;\r
254     }\r
255 }\r