001: /**
002: * Copyright (c) 2001, Sergey A. Samokhodkin
003: * All rights reserved.
004: *
005: * Redistribution and use in source and binary forms, with or without modification,
006: * are permitted provided that the following conditions are met:
007: *
008: * - Redistributions of source code must retain the above copyright notice,
009: * this list of conditions and the following disclaimer.
010: * - Redistributions in binary form
011: * must reproduce the above copyright notice, this list of conditions and the following
012: * disclaimer in the documentation and/or other materials provided with the distribution.
013: * - Neither the name of jregex nor the names of its contributors may be used
014: * to endorse or promote products derived from this software without specific prior
015: * written permission.
016: *
017: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
018: * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
019: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
020: * IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
021: * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
022: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
023: * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
024: * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
025: * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
026: *
027: * @version 1.2_01
028: */package jregex;
029:
030: import java.io.*;
031: import java.util.Hashtable;
032: import java.util.Vector;
033:
034: /**
035: * An implementation of the Substitution interface. Performs substitutions in accordance with Perl-like substitution scripts.<br>
036: * The latter is a string, containing a mix of memory register references and plain text blocks.<br>
037: * It may look like "some_chars $1 some_chars$2some_chars" or "123${1}45${2}67".<br>
038: * A tag consisting of '$',not preceeded by the escape character'\' and followed by some digits (possibly enclosed in the curled brackets) is interpreted as a memory register reference, the digits forming a register ID.
039: * All the rest is considered as a plain text.<br>
040: * Upon the Replacer has found a text block that matches the pattern, a references in a replacement string are replaced by the contents of
041: * corresponding memory registers, and the resulting text replaces the matched block.<br>
042: * For example, the following code:
043: * <pre>
044: * System.out.println("\""+
045: * new Replacer(new Pattern("\\b(\\d+)\\b"),new PerlSubstitution("'$1'")).replace("abc 123 def")
046: * +"\"");
047: * </pre>
048: * will print <code>"abc '123' def"</code>.<br>
049: * @see Substitution
050: * @see Replacer
051: * @see Pattern
052: */
053:
054: public class PerlSubstitution implements Substitution {
055: //private static Pattern refPtn,argsPtn;
056: private static Pattern refPtn;
057: private static int NAME_ID;
058: private static int ESC_ID;
059: //private static int FN_NAME_ID;
060: //private static int FN_ARGS_ID;
061: //private static int ARG_NAME_ID;
062:
063: private static final String groupRef = "\\$(?:\\{({=name}\\w+)\\}|({=name}\\d+|&))|\\\\({esc}.)";
064: //private static final String fnRef="\\&({fn_name}\\w+)\\(({fn_args}"+groupRef+"(?:,"+groupRef+")*)*\\)";
065:
066: static {
067: try {
068: //refPtn=new Pattern("(?<!\\\\)"+fnRef+"|"+groupRef);
069: //argsPtn=new Pattern(groupRef);
070: //refPtn=new Pattern("(?<!\\\\)"+groupRef);
071: refPtn = new Pattern(groupRef);
072: NAME_ID = refPtn.groupId("name").intValue();
073: ESC_ID = refPtn.groupId("esc").intValue();
074: //ARG_NAME_ID=argsPtn.groupId("name").intValue();
075: //FN_NAME_ID=refPtn.groupId("fn_name").intValue();
076: //FN_ARGS_ID=refPtn.groupId("fn_args").intValue();
077: } catch (PatternSyntaxException e) {
078: e.printStackTrace();
079: }
080: }
081:
082: private Element queueEntry;
083:
084: //It seems we should somehow throw an IllegalArgumentException if an expression
085: //holds a reference to a non-existing group. Such checking will require a Pattern instance.
086: public PerlSubstitution(String s) {
087: Matcher refMatcher = new Matcher(refPtn);
088: refMatcher.setTarget(s);
089: queueEntry = makeQueue(refMatcher);
090: }
091:
092: public String value(MatchResult mr) {
093: TextBuffer dest = Replacer.wrap(new StringBuffer(mr.length()));
094: appendSubstitution(mr, dest);
095: return dest.toString();
096: }
097:
098: private static Element makeQueue(Matcher refMatcher) {
099: if (refMatcher.find()) {
100: Element element;
101: if (refMatcher.isCaptured(NAME_ID)) {
102: char c = refMatcher.charAt(0, NAME_ID);
103: if (c == '&') {
104: element = new IntRefHandler(refMatcher.prefix(),
105: new Integer(0));
106: } else if (Character.isDigit(c)) {
107: element = new IntRefHandler(refMatcher.prefix(),
108: new Integer(refMatcher.group(NAME_ID)));
109: } else
110: element = new StringRefHandler(refMatcher.prefix(),
111: refMatcher.group(NAME_ID));
112: } else {
113: //escaped char
114: element = new PlainElement(refMatcher.prefix(),
115: refMatcher.group(ESC_ID));
116: }
117: refMatcher.setTarget(refMatcher, MatchResult.SUFFIX);
118: element.next = makeQueue(refMatcher);
119: return element;
120: } else
121: return new PlainElement(refMatcher.target());
122: }
123:
124: public void appendSubstitution(MatchResult match, TextBuffer dest) {
125: for (Element element = this .queueEntry; element != null; element = element.next) {
126: element.append(match, dest);
127: }
128: }
129:
130: public String toString() {
131: StringBuffer sb = new StringBuffer();
132: for (Element element = this .queueEntry; element != null; element = element.next) {
133: sb.append(element.toString());
134: }
135: return sb.toString();
136: }
137:
138: private static abstract class Element {
139: protected String prefix;
140: Element next;
141:
142: abstract void append(MatchResult match, TextBuffer dest);
143: }
144:
145: private static class PlainElement extends Element {
146: private String str;
147:
148: PlainElement(String s) {
149: str = s;
150: }
151:
152: PlainElement(String pref, String s) {
153: prefix = pref;
154: str = s;
155: }
156:
157: void append(MatchResult match, TextBuffer dest) {
158: if (prefix != null)
159: dest.append(prefix);
160: if (str != null)
161: dest.append(str);
162: }
163: }
164:
165: private static class IntRefHandler extends Element {
166: private Integer index;
167:
168: IntRefHandler(String s, Integer ind) {
169: prefix = s;
170: index = ind;
171: }
172:
173: void append(MatchResult match, TextBuffer dest) {
174: if (prefix != null)
175: dest.append(prefix);
176: if (index == null)
177: return;
178: int i = index.intValue();
179: if (i >= match.pattern().groupCount())
180: return;
181: if (match.isCaptured(i))
182: match.getGroup(i, dest);
183: }
184: }
185:
186: private static class StringRefHandler extends Element {
187: private String index;
188:
189: StringRefHandler(String s, String ind) {
190: prefix = s;
191: index = ind;
192: }
193:
194: void append(MatchResult match, TextBuffer dest) {
195: if (prefix != null)
196: dest.append(prefix);
197: if (index == null)
198: return;
199: Integer id = match.pattern().groupId(index);
200: //if(id==null) return; //???
201: int i = id.intValue();
202: if (match.isCaptured(i))
203: match.getGroup(i, dest);
204: }
205: }
206: }
207:
208: abstract class GReference {
209: public abstract String stringValue(MatchResult match);
210:
211: public static GReference createInstance(MatchResult match, int grp) {
212: if (match.length(grp) == 0)
213: throw new IllegalArgumentException(
214: "arg name cannot be an empty string");
215: if (Character.isDigit(match.charAt(0, grp))) {
216: try {
217: return new IntReference(Integer.parseInt(match
218: .group(grp)));
219: } catch (NumberFormatException e) {
220: throw new IllegalArgumentException(
221: "illegal arg name, starts with digit but is not a number");
222: }
223: }
224: return new StringReference((match.group(grp)));
225: }
226: }
227:
228: class IntReference extends GReference {
229: protected int id;
230:
231: IntReference(int id) {
232: this .id = id;
233: }
234:
235: public String stringValue(MatchResult match) {
236: return match.group(id);
237: }
238: }
239:
240: class StringReference extends GReference {
241: protected String name;
242:
243: StringReference(String name) {
244: this .name = name;
245: }
246:
247: public String stringValue(MatchResult match) {
248: return match.group(name);
249: }
250: }
|