-//\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
+//
+// 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 <a
+ * href="http://javaregex.com/code/fancy.java.html">example</a>
+ *
+ * @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/ ... / <i>The
+ * argument to ReplaceRule.perlCode</i> /.
+ */
+ 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;
+ }
+}