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