X-Git-Url: http://source.jalview.org/gitweb/?a=blobdiff_plain;f=src%2Fcom%2Fstevesoft%2Fpat%2FReplaceRule.java;h=6f44db61275f3c2bbf58f5ce9e9f67ae60ef2446;hb=02816cb583d01a3a0cdd612d5d0a1f24c0d0b99d;hp=002ffa1e4feab326bda208e5d6bb8d15ab0cd7f0;hpb=c40cf903f740a72ab63dd1abc10fa33450ce660d;p=jalview.git
diff --git a/src/com/stevesoft/pat/ReplaceRule.java b/src/com/stevesoft/pat/ReplaceRule.java
index 002ffa1..6f44db6 100755
--- a/src/com/stevesoft/pat/ReplaceRule.java
+++ b/src/com/stevesoft/pat/ReplaceRule.java
@@ -1,255 +1,400 @@
-//
-// This software is now distributed according to
-// the Lesser Gnu Public License. Please see
-// http://www.gnu.org/copyleft/lesser.txt for
-// the details.
-// -- Happy Computing!
-//
-package com.stevesoft.pat;
-
-import java.util.*;
-
-/** ReplaceRule is a singly linked list of Objects which describe how
- to replace the matched portion of a String. The only member method
- that you absolutely need to define to use this class is apply(StringBuffer,RegRes) --
- although you may want define toString1() and clone1() (if you are
- unhappy with the default methods) that are needed by
- the clone() or toString() methods on this class.
- During the replacement process, each ReplaceRule tells the replacer
- what to add to StringBuffer and uses the contents of the Regular
- expression result to get the information it needs to
- do this. Here is an example
-
- @see com.stevesoft.pat.NullRule
- @see com.stevesoft.pat.AmpersandRule
- @see com.stevesoft.pat.BackRefRule
- @see com.stevesoft.pat.LeftRule
- @see com.stevesoft.pat.RightRule
- @see com.stevesoft.pat.StringRule
- */
-public abstract class ReplaceRule {
- /** points to the next ReplaceRule in the linked list. */
- protected ReplaceRule next = null;
- /** This function appends to the StringBufferLike the text you want
- to replaced the portion of the String last matched. */
- public abstract void apply(StringBufferLike sb,RegRes r);
-
- /** A rule describing how to clone only the current ReplaceRule,
- and none of the others in this linked list. It is called by
- clone() for each item in the list. */
- public Object clone1() {
- return new RuleHolder(this);
- }
- public final Object clone() {
- ReplaceRule x = (ReplaceRule)clone1();
- ReplaceRule xsav = x;
- ReplaceRule y = this;
- while(y.next != null) {
- x.next = (ReplaceRule)y.next.clone1();
- x.name = y.name;
- x = x.next;
- y = y.next;
- }
- return xsav;
- }
- static ReplaceRule add(ReplaceRule head,ReplaceRule adding) {
- if(head == null)
- return head = adding;
- head.addRule(adding);
- return head;
- }
- public ReplaceRule add(ReplaceRule adding) {
- return add(this,adding);
- }
- /** Add another ReplaceRule to the linked list. */
- public void addRule(ReplaceRule r) {
- if(next == null) next = r;
- else next.addRule(r);
- }
- static Regex getvar = null;
- final static Regex getv() {
- // Thanks to Michael Jimenez for pointing out the need
- // to clone getvar rather than simply returning it.
- // Previously this was not thread safe.
- //if(getvar != null) return getvar;
- if(getvar != null) return (Regex)getvar.clone();
- getvar=
- new Regex(
- "(?:\\\\(\\d+)|"+ // ref 1
- "\\$(?:"+
- "(\\d+)|"+ // ref 2
- "(\\w+)|"+ // ref 3
- "([&'`])|"+ // ref 4
- "\\{(?:(\\d+)|"+ // ref 5
- "([^\n}\\\\]+))}"+ // ref 6
- ")|"+
- "\\\\([nrbtaef])|"+ // ref 7
- "\\\\c([\u0000-\uFFFF])|"+ // ref 8
- "\\\\x([A-Fa-f0-9]{2})|"+ // ref 9
- "\\\\([\u0000-\uFFFF])"+ // ref 10
- ")");
- getvar.optimize();
- return getvar;
- }
- /** Compile a ReplaceRule using the text that would go between
- the second and third /'s in a typical substitution pattern
- in Perl: s/ ... / The argument to ReplaceRule.perlCode /.
- */
- public static ReplaceRule perlCode(String s) {
- //String sav_backGs = Regex.backGs;
- //int sav_backGto = Regex.backGto;
- try {
- int mf = 0, mt = 0;
- Regex gv = getv();
- ReplaceRule head = null;
- Object tmp = null;
- while(gv.searchFrom(s,mt)) {
- int off=Regex.BackRefOffset-1;
- mf = gv.matchedFrom();
- if(mf > mt)
- head=add(head,
- new StringRule(s.substring(mt,mf)));
- String var = null;
- if((var=gv.stringMatched(1+off)) != null
- || (var=gv.stringMatched(2+off)) != null
- || (var=gv.stringMatched(5+off)) != null) {
- int d=0;
- for(int i=0;i= 0)
- head=add(head,new CodeRule(var.charAt(0)) );
- else
- head=add(head,new StringRule(var) );
- } else if(
- (var=gv.stringMatched(3+off)) != null
- || (var=gv.stringMatched(4+off)) != null
- || (var=gv.stringMatched(6+off)) != null) {
- String arg = "";
- int pc;
- if((pc=var.indexOf(':')) > 0) {
- arg = var.substring(pc+1);
- var = var.substring(0,pc);
- }
- if(var.equals("&")||var.equals("MATCH")) {
- head=add(head,new AmpersandRule());
- } else if(var.equals("`")||var.equals("PREMATCH")) {
- head=add(head,new LeftRule());
- } else if(var.equals("'")||var.equals("POSTMATCH")) {
- head=add(head,new RightRule());
- } else if(var.equals("WANT_MORE_TEXT")) {
- head=add(head,new WantMoreTextReplaceRule());
- } else if(var.equals("POP")) {
- head=add(head,new PopRule());
- } else if(var.startsWith("+") && (tmp=defs.get(var.substring(1))) != null) {
- if(tmp instanceof Regex)
- head=add(head,new PushRule(var.substring(1),(Regex)tmp));
- else if(tmp instanceof Transformer)
- head=add(head,new PushRule(var.substring(1),(Transformer)tmp));
- else head=add(head,new StringRule("${"+var+"}"));
- } else if(var.startsWith("=") && (tmp=defs.get(var.substring(1))) != null) {
- if(tmp instanceof Regex)
- head=add(head,new ChangeRule(var.substring(1),(Regex)tmp));
- else if(tmp instanceof Transformer)
- head=add(head,new ChangeRule(var.substring(1),(Transformer)tmp));
- else head=add(head,new StringRule("${"+var+"}"));
- } else if( (tmp=defs.get(var)) != null) {
- if(tmp instanceof ReplaceRule) {
- ReplaceRule alt = ((ReplaceRule)tmp).arg(arg);
- if(alt == null) alt = ((ReplaceRule)tmp);
- head=add(head,(ReplaceRule)(alt.clone()));
- }
- } else // can't figure out how to transform this thing...
- head=add(head,new StringRule("${"+var+"}"));
- } else if(
- (var = gv.stringMatched(7+off)) != null) {
- char c = var.charAt(0);
- if(c == 'n')
- head=add(head,new StringRule("\n"));
- else if(c == 't')
- head=add(head,new StringRule("\t"));
- else if(c == 'r')
- head=add(head,new StringRule("\r"));
- else if(c == 'b')
- head=add(head,new StringRule("\r"));
- else if(c == 'a')
- head=add(head,new StringRule(""+(char)7));
- else if(c == 'e')
- head=add(head,new StringRule(""+(char)27));
- else if(c == 'f')
- head=add(head,new StringRule(""+(char)12));
- } else if(
- (var = gv.stringMatched(8+off)) != null) {
- char c = var.charAt(0);
- if(c < Ctrl.cmap.length)
- c = Ctrl.cmap[c];
- head=add(head,new StringRule(""+c));
- } else if(
- (var = gv.stringMatched(9+off)) != null) {
- int d =
- 16*getHexDigit(var.charAt(0))+
- getHexDigit(var.charAt(1));
- head=add(head,new StringRule(""+(char)d));
- }
- mt = gv.matchedTo();
- }
- if(mt <= s.length())
- head=add(head,new StringRule(s.substring(mt)));
- return head;
- } finally {
- //Regex.backGs = sav_backGs;
- //Regex.backGto = sav_backGto;
- }
- }
- static Hashtable defs = new Hashtable();
- public static boolean isDefined(String s) { return defs.get(s) != null; }
- public static void define(String s,Regex r) { defs.put(s,r); }
- public static void define(String s,ReplaceRule r) {
- defs.put(s,r);
- r.name = s;
- }
- String name = getClass().getName();
- public static void define(String s,Transformer t) { defs.put(s,t); }
- public static void undefine(String s) { defs.remove(s); }
- /** This tells how to convert just the current element (and none
- of the other items in the linked list) to a String. This
- method is called by toString() for each item in the linked
- list. */
- public String toString1() {
- return "${"+name+"}";
- }
- /** Convert to a String. */
- public final String toString() {
- StringBuffer sb = new StringBuffer();
- sb.append(toString1());
- ReplaceRule rr = this.next;
- while(rr != null) {
- sb.append(rr.toString1());
- rr = rr.next;
- }
- return sb.toString();
- }
- /** Modified the behavior of a ReplaceRule by supplying
- an argument. If a ReplaceRule named "foo" is defined
- and the pattern "s/x/${foo:5}/" is given to Regex.perlCode,
- then the "foo" the definition of "foo" will be retrieved
- and arg("5") will be called. If the result is non-null,
- that is the ReplaceRule that will be used. If the result
- is null, then the pattern works just as if it were
- "s/x/${foo}/".
- @see com.stevesoft.pat.Validator#arg(java.lang.String)
- */
- public ReplaceRule arg(String s) { return null; }
- static int getHexDigit(char c) {
- if(c >= '0' && c <= '9')
- return c - '0';
- if(c >= 'a' && c <= 'f')
- return c - 'a'+10;
- return c - 'A'+10;
- }
-}
+//
+// This software is now distributed according to
+// the Lesser Gnu Public License. Please see
+// http://www.gnu.org/copyleft/lesser.txt for
+// the details.
+// -- Happy Computing!
+//
+package com.stevesoft.pat;
+
+import java.util.Hashtable;
+
+/**
+ * ReplaceRule is a singly linked list of Objects which describe how to replace
+ * the matched portion of a String. The only member method that you absolutely
+ * need to define to use this class is apply(StringBuffer,RegRes) -- although
+ * you may want define toString1() and clone1() (if you are unhappy with the
+ * default methods) that are needed by the clone() or toString() methods on this
+ * class. During the replacement process, each ReplaceRule tells the replacer
+ * what to add to StringBuffer and uses the contents of the Regular expression
+ * result to get the information it needs to do this. Here is an
+ * example
+ *
+ * @see com.stevesoft.pat.NullRule
+ * @see com.stevesoft.pat.AmpersandRule
+ * @see com.stevesoft.pat.BackRefRule
+ * @see com.stevesoft.pat.LeftRule
+ * @see com.stevesoft.pat.RightRule
+ * @see com.stevesoft.pat.StringRule
+ */
+public abstract class ReplaceRule
+{
+ /** points to the next ReplaceRule in the linked list. */
+ protected ReplaceRule next = null;
+
+ /**
+ * This function appends to the StringBufferLike the text you want to replaced
+ * the portion of the String last matched.
+ */
+ public abstract void apply(StringBufferLike sb, RegRes r);
+
+ /**
+ * A rule describing how to clone only the current ReplaceRule, and none of
+ * the others in this linked list. It is called by clone() for each item in
+ * the list.
+ */
+ public Object clone1()
+ {
+ return new RuleHolder(this);
+ }
+
+ public final Object clone()
+ {
+ ReplaceRule x = (ReplaceRule) clone1();
+ ReplaceRule xsav = x;
+ ReplaceRule y = this;
+ while (y.next != null)
+ {
+ x.next = (ReplaceRule) y.next.clone1();
+ x.name = y.name;
+ x = x.next;
+ y = y.next;
+ }
+ return xsav;
+ }
+
+ static ReplaceRule add(ReplaceRule head, ReplaceRule adding)
+ {
+ if (head == null)
+ {
+ return head = adding;
+ }
+ head.addRule(adding);
+ return head;
+ }
+
+ public ReplaceRule add(ReplaceRule adding)
+ {
+ return add(this, adding);
+ }
+
+ /** Add another ReplaceRule to the linked list. */
+ public void addRule(ReplaceRule r)
+ {
+ if (next == null)
+ {
+ next = r;
+ }
+ else
+ {
+ next.addRule(r);
+ }
+ }
+
+ static Regex getvar = null;
+
+ final static Regex getv()
+ {
+ // Thanks to Michael Jimenez for pointing out the need
+ // to clone getvar rather than simply returning it.
+ // Previously this was not thread safe.
+ // if(getvar != null) return getvar;
+ if (getvar != null)
+ {
+ return (Regex) getvar.clone();
+ }
+ getvar = new Regex("(?:\\\\(\\d+)|" + // ref 1
+ "\\$(?:" + "(\\d+)|" + // ref 2
+ "(\\w+)|" + // ref 3
+ "([&'`])|" + // ref 4
+ "\\{(?:(\\d+)|" + // ref 5
+ "([^\n}\\\\]+))}" + // ref 6
+ ")|" + "\\\\([nrbtaef])|" + // ref 7
+ "\\\\c([\u0000-\uFFFF])|" + // ref 8
+ "\\\\x([A-Fa-f0-9]{2})|" + // ref 9
+ "\\\\([\u0000-\uFFFF])" + // ref 10
+ ")");
+ getvar.optimize();
+ return getvar;
+ }
+
+ /**
+ * Compile a ReplaceRule using the text that would go between the second and
+ * third /'s in a typical substitution pattern in Perl: s/ ... / The
+ * argument to ReplaceRule.perlCode /.
+ */
+ public static ReplaceRule perlCode(String s)
+ {
+ // String sav_backGs = Regex.backGs;
+ // int sav_backGto = Regex.backGto;
+ try
+ {
+ int mf = 0, mt = 0;
+ Regex gv = getv();
+ ReplaceRule head = null;
+ Object tmp = null;
+ while (gv.searchFrom(s, mt))
+ {
+ int off = Regex.BackRefOffset - 1;
+ mf = gv.matchedFrom();
+ if (mf > mt)
+ {
+ head = add(head, new StringRule(s.substring(mt, mf)));
+ }
+ String var = null;
+ if ((var = gv.stringMatched(1 + off)) != null
+ || (var = gv.stringMatched(2 + off)) != null
+ || (var = gv.stringMatched(5 + off)) != null)
+ {
+ int d = 0;
+ for (int i = 0; i < var.length(); i++)
+ {
+ d = 8 * d + (var.charAt(i) - '0');
+ }
+ if (var.length() == 1)
+ {
+ head = add(head, new BackRefRule(d));
+ }
+ else
+ {
+ head = new StringRule("" + (char) d);
+ }
+ }
+ else if ((var = gv.stringMatched(10 + off)) != null)
+ {
+ if ("QELlUu".indexOf(var) >= 0)
+ {
+ head = add(head, new CodeRule(var.charAt(0)));
+ }
+ else
+ {
+ head = add(head, new StringRule(var));
+ }
+ }
+ else if ((var = gv.stringMatched(3 + off)) != null
+ || (var = gv.stringMatched(4 + off)) != null
+ || (var = gv.stringMatched(6 + off)) != null)
+ {
+ String arg = "";
+ int pc;
+ if ((pc = var.indexOf(':')) > 0)
+ {
+ arg = var.substring(pc + 1);
+ var = var.substring(0, pc);
+ }
+ if (var.equals("&") || var.equals("MATCH"))
+ {
+ head = add(head, new AmpersandRule());
+ }
+ else if (var.equals("`") || var.equals("PREMATCH"))
+ {
+ head = add(head, new LeftRule());
+ }
+ else if (var.equals("'") || var.equals("POSTMATCH"))
+ {
+ head = add(head, new RightRule());
+ }
+ else if (var.equals("WANT_MORE_TEXT"))
+ {
+ head = add(head, new WantMoreTextReplaceRule());
+ }
+ else if (var.equals("POP"))
+ {
+ head = add(head, new PopRule());
+ }
+ else if (var.startsWith("+")
+ && (tmp = defs.get(var.substring(1))) != null)
+ {
+ if (tmp instanceof Regex)
+ {
+ head = add(head, new PushRule(var.substring(1), (Regex) tmp));
+ }
+ else if (tmp instanceof Transformer)
+ {
+ head = add(head,
+ new PushRule(var.substring(1), (Transformer) tmp));
+ }
+ else
+ {
+ head = add(head, new StringRule("${" + var + "}"));
+ }
+ }
+ else if (var.startsWith("=")
+ && (tmp = defs.get(var.substring(1))) != null)
+ {
+ if (tmp instanceof Regex)
+ {
+ head = add(head,
+ new ChangeRule(var.substring(1), (Regex) tmp));
+ }
+ else if (tmp instanceof Transformer)
+ {
+ head = add(head,
+ new ChangeRule(var.substring(1), (Transformer) tmp));
+ }
+ else
+ {
+ head = add(head, new StringRule("${" + var + "}"));
+ }
+ }
+ else if ((tmp = defs.get(var)) != null)
+ {
+ if (tmp instanceof ReplaceRule)
+ {
+ ReplaceRule alt = ((ReplaceRule) tmp).arg(arg);
+ if (alt == null)
+ {
+ alt = ((ReplaceRule) tmp);
+ }
+ head = add(head, (ReplaceRule) (alt.clone()));
+ }
+ }
+ else
+ // can't figure out how to transform this thing...
+ {
+ head = add(head, new StringRule("${" + var + "}"));
+ }
+ }
+ else if ((var = gv.stringMatched(7 + off)) != null)
+ {
+ char c = var.charAt(0);
+ if (c == 'n')
+ {
+ head = add(head, new StringRule("\n"));
+ }
+ else if (c == 't')
+ {
+ head = add(head, new StringRule("\t"));
+ }
+ else if (c == 'r')
+ {
+ head = add(head, new StringRule("\r"));
+ }
+ else if (c == 'b')
+ {
+ head = add(head, new StringRule("\r"));
+ }
+ else if (c == 'a')
+ {
+ head = add(head, new StringRule("" + (char) 7));
+ }
+ else if (c == 'e')
+ {
+ head = add(head, new StringRule("" + (char) 27));
+ }
+ else if (c == 'f')
+ {
+ head = add(head, new StringRule("" + (char) 12));
+ }
+ }
+ else if ((var = gv.stringMatched(8 + off)) != null)
+ {
+ char c = var.charAt(0);
+ if (c < Ctrl.cmap.length)
+ {
+ c = Ctrl.cmap[c];
+ }
+ head = add(head, new StringRule("" + c));
+ }
+ else if ((var = gv.stringMatched(9 + off)) != null)
+ {
+ int d = 16 * getHexDigit(var.charAt(0))
+ + getHexDigit(var.charAt(1));
+ head = add(head, new StringRule("" + (char) d));
+ }
+ mt = gv.matchedTo();
+ }
+ if (mt <= s.length())
+ {
+ head = add(head, new StringRule(s.substring(mt)));
+ }
+ return head;
+ } finally
+ {
+ // Regex.backGs = sav_backGs;
+ // Regex.backGto = sav_backGto;
+ }
+ }
+
+ static Hashtable defs = new Hashtable();
+
+ public static boolean isDefined(String s)
+ {
+ return defs.get(s) != null;
+ }
+
+ public static void define(String s, Regex r)
+ {
+ defs.put(s, r);
+ }
+
+ public static void define(String s, ReplaceRule r)
+ {
+ defs.put(s, r);
+ r.name = s;
+ }
+
+ String name = getClass().getName();
+
+ public static void define(String s, Transformer t)
+ {
+ defs.put(s, t);
+ }
+
+ public static void undefine(String s)
+ {
+ defs.remove(s);
+ }
+
+ /**
+ * This tells how to convert just the current element (and none of the other
+ * items in the linked list) to a String. This method is called by toString()
+ * for each item in the linked list.
+ */
+ public String toString1()
+ {
+ return "${" + name + "}";
+ }
+
+ /** Convert to a String. */
+ public final String toString()
+ {
+ StringBuffer sb = new StringBuffer();
+ sb.append(toString1());
+ ReplaceRule rr = this.next;
+ while (rr != null)
+ {
+ sb.append(rr.toString1());
+ rr = rr.next;
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Modified the behavior of a ReplaceRule by supplying an argument. If a
+ * ReplaceRule named "foo" is defined and the pattern "s/x/${foo:5}/" is given
+ * to Regex.perlCode, then the "foo" the definition of "foo" will be retrieved
+ * and arg("5") will be called. If the result is non-null, that is the
+ * ReplaceRule that will be used. If the result is null, then the pattern
+ * works just as if it were "s/x/${foo}/".
+ *
+ * @see com.stevesoft.pat.Validator#arg(java.lang.String)
+ */
+ public ReplaceRule arg(String s)
+ {
+ return null;
+ }
+
+ static int getHexDigit(char c)
+ {
+ if (c >= '0' && c <= '9')
+ {
+ return c - '0';
+ }
+ if (c >= 'a' && c <= 'f')
+ {
+ return c - 'a' + 10;
+ }
+ return c - 'A' + 10;
+ }
+}