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;
+ }
+}