1 /*******************************************************************************
2 * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3 * Copyright (C) $(date) The Jalview Authors
5 * This file is part of Jalview.
7 * Jalview is free software: you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation, either version 3
10 * of the License, or (at your option) any later version.
12 * Jalview is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty
14 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 * PURPOSE. See the GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with Jalview. If not, see <http://www.gnu.org/licenses/>.
19 * The Jalview Authors are detailed in the 'AUTHORS' file.
20 *******************************************************************************/
22 // This software is now distributed according to
23 // the Lesser Gnu Public License. Please see
24 // http://www.gnu.org/copyleft/lesser.txt for
26 // -- Happy Computing!
28 package com.stevesoft.pat;
30 import java.util.Hashtable;
33 * ReplaceRule is a singly linked list of Objects which describe how to replace
34 * the matched portion of a String. The only member method that you absolutely
35 * need to define to use this class is apply(StringBuffer,RegRes) -- although
36 * you may want define toString1() and clone1() (if you are unhappy with the
37 * default methods) that are needed by the clone() or toString() methods on this
38 * class. During the replacement process, each ReplaceRule tells the replacer
39 * what to add to StringBuffer and uses the contents of the Regular expression
40 * result to get the information it needs to do this. Here is an <a
41 * href="http://javaregex.com/code/fancy.java.html">example</a>
43 * @see com.stevesoft.pat.NullRule
44 * @see com.stevesoft.pat.AmpersandRule
45 * @see com.stevesoft.pat.BackRefRule
46 * @see com.stevesoft.pat.LeftRule
47 * @see com.stevesoft.pat.RightRule
48 * @see com.stevesoft.pat.StringRule
50 public abstract class ReplaceRule
52 /** points to the next ReplaceRule in the linked list. */
53 protected ReplaceRule next = null;
56 * This function appends to the StringBufferLike the text you want to replaced
57 * the portion of the String last matched.
59 public abstract void apply(StringBufferLike sb, RegRes r);
62 * A rule describing how to clone only the current ReplaceRule, and none of
63 * the others in this linked list. It is called by clone() for each item in
66 public Object clone1()
68 return new RuleHolder(this);
71 public final Object clone()
73 ReplaceRule x = (ReplaceRule) clone1();
76 while (y.next != null)
78 x.next = (ReplaceRule) y.next.clone1();
86 static ReplaceRule add(ReplaceRule head, ReplaceRule adding)
96 public ReplaceRule add(ReplaceRule adding)
98 return add(this, adding);
101 /** Add another ReplaceRule to the linked list. */
102 public void addRule(ReplaceRule r)
114 static Regex getvar = null;
116 final static Regex getv()
118 // Thanks to Michael Jimenez for pointing out the need
119 // to clone getvar rather than simply returning it.
120 // Previously this was not thread safe.
121 // if(getvar != null) return getvar;
124 return (Regex) getvar.clone();
126 getvar = new Regex("(?:\\\\(\\d+)|" + // ref 1
127 "\\$(?:" + "(\\d+)|" + // ref 2
129 "([&'`])|" + // ref 4
130 "\\{(?:(\\d+)|" + // ref 5
131 "([^\n}\\\\]+))}" + // ref 6
132 ")|" + "\\\\([nrbtaef])|" + // ref 7
133 "\\\\c([\u0000-\uFFFF])|" + // ref 8
134 "\\\\x([A-Fa-f0-9]{2})|" + // ref 9
135 "\\\\([\u0000-\uFFFF])" + // ref 10
142 * Compile a ReplaceRule using the text that would go between the second and
143 * third /'s in a typical substitution pattern in Perl: s/ ... / <i>The
144 * argument to ReplaceRule.perlCode</i> /.
146 public static ReplaceRule perlCode(String s)
148 // String sav_backGs = Regex.backGs;
149 // int sav_backGto = Regex.backGto;
154 ReplaceRule head = null;
156 while (gv.searchFrom(s, mt))
158 int off = Regex.BackRefOffset - 1;
159 mf = gv.matchedFrom();
162 head = add(head, new StringRule(s.substring(mt, mf)));
165 if ((var = gv.stringMatched(1 + off)) != null
166 || (var = gv.stringMatched(2 + off)) != null
167 || (var = gv.stringMatched(5 + off)) != null)
170 for (int i = 0; i < var.length(); i++)
172 d = 8 * d + (var.charAt(i) - '0');
174 if (var.length() == 1)
176 head = add(head, new BackRefRule(d));
180 head = new StringRule("" + (char) d);
183 else if ((var = gv.stringMatched(10 + off)) != null)
185 if ("QELlUu".indexOf(var) >= 0)
187 head = add(head, new CodeRule(var.charAt(0)));
191 head = add(head, new StringRule(var));
194 else if ((var = gv.stringMatched(3 + off)) != null
195 || (var = gv.stringMatched(4 + off)) != null
196 || (var = gv.stringMatched(6 + off)) != null)
200 if ((pc = var.indexOf(':')) > 0)
202 arg = var.substring(pc + 1);
203 var = var.substring(0, pc);
205 if (var.equals("&") || var.equals("MATCH"))
207 head = add(head, new AmpersandRule());
209 else if (var.equals("`") || var.equals("PREMATCH"))
211 head = add(head, new LeftRule());
213 else if (var.equals("'") || var.equals("POSTMATCH"))
215 head = add(head, new RightRule());
217 else if (var.equals("WANT_MORE_TEXT"))
219 head = add(head, new WantMoreTextReplaceRule());
221 else if (var.equals("POP"))
223 head = add(head, new PopRule());
225 else if (var.startsWith("+")
226 && (tmp = defs.get(var.substring(1))) != null)
228 if (tmp instanceof Regex)
230 head = add(head, new PushRule(var.substring(1), (Regex) tmp));
232 else if (tmp instanceof Transformer)
234 head = add(head, new PushRule(var.substring(1),
239 head = add(head, new StringRule("${" + var + "}"));
242 else if (var.startsWith("=")
243 && (tmp = defs.get(var.substring(1))) != null)
245 if (tmp instanceof Regex)
248 new ChangeRule(var.substring(1), (Regex) tmp));
250 else if (tmp instanceof Transformer)
252 head = add(head, new ChangeRule(var.substring(1),
257 head = add(head, new StringRule("${" + var + "}"));
260 else if ((tmp = defs.get(var)) != null)
262 if (tmp instanceof ReplaceRule)
264 ReplaceRule alt = ((ReplaceRule) tmp).arg(arg);
267 alt = ((ReplaceRule) tmp);
269 head = add(head, (ReplaceRule) (alt.clone()));
273 // can't figure out how to transform this thing...
275 head = add(head, new StringRule("${" + var + "}"));
278 else if ((var = gv.stringMatched(7 + off)) != null)
280 char c = var.charAt(0);
283 head = add(head, new StringRule("\n"));
287 head = add(head, new StringRule("\t"));
291 head = add(head, new StringRule("\r"));
295 head = add(head, new StringRule("\r"));
299 head = add(head, new StringRule("" + (char) 7));
303 head = add(head, new StringRule("" + (char) 27));
307 head = add(head, new StringRule("" + (char) 12));
310 else if ((var = gv.stringMatched(8 + off)) != null)
312 char c = var.charAt(0);
313 if (c < Ctrl.cmap.length)
317 head = add(head, new StringRule("" + c));
319 else if ((var = gv.stringMatched(9 + off)) != null)
321 int d = 16 * getHexDigit(var.charAt(0))
322 + getHexDigit(var.charAt(1));
323 head = add(head, new StringRule("" + (char) d));
327 if (mt <= s.length())
329 head = add(head, new StringRule(s.substring(mt)));
334 // Regex.backGs = sav_backGs;
335 // Regex.backGto = sav_backGto;
339 static Hashtable defs = new Hashtable();
341 public static boolean isDefined(String s)
343 return defs.get(s) != null;
346 public static void define(String s, Regex r)
351 public static void define(String s, ReplaceRule r)
357 String name = getClass().getName();
359 public static void define(String s, Transformer t)
364 public static void undefine(String s)
370 * This tells how to convert just the current element (and none of the other
371 * items in the linked list) to a String. This method is called by toString()
372 * for each item in the linked list.
374 public String toString1()
376 return "${" + name + "}";
379 /** Convert to a String. */
380 public final String toString()
382 StringBuffer sb = new StringBuffer();
383 sb.append(toString1());
384 ReplaceRule rr = this.next;
387 sb.append(rr.toString1());
390 return sb.toString();
394 * Modified the behavior of a ReplaceRule by supplying an argument. If a
395 * ReplaceRule named "foo" is defined and the pattern "s/x/${foo:5}/" is given
396 * to Regex.perlCode, then the "foo" the definition of "foo" will be retrieved
397 * and arg("5") will be called. If the result is non-null, that is the
398 * ReplaceRule that will be used. If the result is null, then the pattern
399 * works just as if it were "s/x/${foo}/".
401 * @see com.stevesoft.pat.Validator#arg(java.lang.String)
403 public ReplaceRule arg(String s)
408 static int getHexDigit(char c)
410 if (c >= '0' && c <= '9')
414 if (c >= 'a' && c <= 'f')