X-Git-Url: http://source.jalview.org/gitweb/?a=blobdiff_plain;f=src%2Fcom%2Fstevesoft%2Fpat%2FReplaceRule.java;fp=src%2Fcom%2Fstevesoft%2Fpat%2FReplaceRule.java;h=002ffa1e4feab326bda208e5d6bb8d15ab0cd7f0;hb=c40cf903f740a72ab63dd1abc10fa33450ce660d;hp=0000000000000000000000000000000000000000;hpb=5bcae030b489e670c6983aa97eb9b6d8a6bbbbd5;p=jalview.git diff --git a/src/com/stevesoft/pat/ReplaceRule.java b/src/com/stevesoft/pat/ReplaceRule.java new file mode 100755 index 0000000..002ffa1 --- /dev/null +++ b/src/com/stevesoft/pat/ReplaceRule.java @@ -0,0 +1,255 @@ +// +// 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; + } +}