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