X-Git-Url: http://source.jalview.org/gitweb/?a=blobdiff_plain;f=src%2Fcom%2Fstevesoft%2Fpat%2FReplaceRule.java;h=74a7a258cbf3cc8275ace069617321b3b599e179;hb=4d7f98a6dd54d9863ba449ec79dcd95d25ed863d;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..74a7a25 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; + } +}