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