001: /*______________________________________________________________________________
002: *
003: * Macker http://innig.net/macker/
004: *
005: * Copyright 2002-2003 Paul Cantrell
006: *
007: * This program is free software; you can redistribute it and/or modify it under
008: * the terms of the GNU General Public License version 2, as published by the
009: * Free Software Foundation. See the file LICENSE.html for more information.
010: *
011: * This program is distributed in the hope that it will be useful, but WITHOUT
012: * ANY WARRANTY, including the implied warranty of MERCHANTABILITY or FITNESS
013: * FOR A PARTICULAR PURPOSE. See the license for more details.
014: *
015: * You should have received a copy of the GNU General Public License along with
016: * this program; if not, write to the Free Software Foundation, Inc. / 59 Temple
017: * Place, Suite 330 / Boston, MA 02111-1307 / USA.
018: *______________________________________________________________________________
019: */
020:
021: package net.innig.macker.rule;
022:
023: import java.util.*;
024:
025: import org.apache.regexp.RE;
026: import org.apache.regexp.RESyntaxException;
027:
028: public final class MackerRegex {
029: //--------------------------------------------------------------------------
030: // Constructors
031: //--------------------------------------------------------------------------
032:
033: public MackerRegex(String regexStr)
034: throws MackerRegexSyntaxException {
035: this (regexStr, true);
036: }
037:
038: public MackerRegex(String regexStr, boolean allowParts)
039: throws MackerRegexSyntaxException {
040: if (regexStr == null)
041: throw new NullPointerException("regexStr == null");
042: buildStaticPatterns();
043:
044: this .regexStr = regexStr;
045: parts = null;
046: regex = null;
047: prevVarValues = new HashMap();
048:
049: if (!(allowParts ? allowable : allowableNoParts)
050: .match(regexStr))
051: throw new MackerRegexSyntaxException(regexStr);
052: }
053:
054: //--------------------------------------------------------------------------
055: // Properties
056: //--------------------------------------------------------------------------
057:
058: public String getPatternString() {
059: return regexStr;
060: }
061:
062: private final String regexStr;
063:
064: //--------------------------------------------------------------------------
065: // Evaluation
066: //--------------------------------------------------------------------------
067:
068: public boolean matches(EvaluationContext context, String s)
069: throws UndeclaredVariableException,
070: MackerRegexSyntaxException {
071: return getMatch(context, s) != null;
072: }
073:
074: public String getMatch(EvaluationContext context, String s)
075: throws UndeclaredVariableException,
076: MackerRegexSyntaxException {
077: parseExpr(context);
078: Boolean match = (Boolean) matchCache.get(s);
079: if (Boolean.FALSE.equals(match))
080: return null;
081: if (Boolean.TRUE.equals(match))
082: return (String) matchResultCache.get(s);
083:
084: match = regex.match('.' + s) ? Boolean.TRUE : Boolean.FALSE;
085: matchCache.put(s, match);
086: if (match.booleanValue()) {
087: String matchResult = regex
088: .getParen(regex.getParenCount() - 1);
089: matchResultCache.put(s, matchResult);
090: return matchResult;
091: } else
092: return null;
093: }
094:
095: private void parseExpr(EvaluationContext context)
096: throws UndeclaredVariableException,
097: MackerRegexSyntaxException {
098: buildStaticPatterns();
099:
100: if (parts == null) {
101: parts = new ArrayList();
102: for (int pos = 0; pos >= 0;) {
103: boolean hasAnotherVar = var.match(regexStr, pos);
104: int expEnd = hasAnotherVar ? var.getParenStart(0)
105: : regexStr.length();
106:
107: if (pos < expEnd)
108: parts.add(new ExpPart(parseSubexpr(regexStr
109: .substring(pos, expEnd))));
110: if (hasAnotherVar)
111: parts.add(new VarPart(var.getParen(1)));
112:
113: pos = hasAnotherVar ? var.getParenEnd(0) : -1;
114: }
115: }
116:
117: // Building the regexp is expensive; there's no point in doing it if we
118: // already have one cached, and the relevant variables haven't changed
119:
120: boolean changed = (regex == null);
121: for (Iterator i = prevVarValues.entrySet().iterator(); !changed
122: && i.hasNext();) {
123: Map.Entry entry = (Map.Entry) i.next();
124: String name = (String) entry.getKey();
125: String value = (String) entry.getValue();
126: if (!context.getVariableValue(name).equals(value))
127: changed = true;
128: }
129:
130: if (changed) {
131: StringBuffer builtRegexStr = new StringBuffer("^\\.?");
132: for (Iterator i = parts.iterator(); i.hasNext();) {
133: Part part = (Part) i.next();
134: if (part instanceof VarPart) {
135: String varName = ((VarPart) part).varName;
136: String varValue = context.getVariableValue(varName);
137: prevVarValues.put(varName, varValue);
138: builtRegexStr.append(parseSubexpr(varValue));
139: } else if (part instanceof ExpPart)
140: builtRegexStr.append(((ExpPart) part).exp);
141: }
142: builtRegexStr.append('$');
143:
144: try {
145: regex = new RE(builtRegexStr.toString());
146: } catch (RESyntaxException rese) {
147: System.out.println("builtRegexStr = " + builtRegexStr);
148: throw new MackerRegexSyntaxException(regexStr, rese);
149: }
150:
151: //! if(???)
152: //! throw new MackerRegexSyntaxException(regexStr, "Too many parenthesized expressions");
153: matchCache = new HashMap();
154: matchResultCache = new HashMap();
155: }
156: }
157:
158: private String parseSubexpr(String exp) {
159: exp = partBoundary.subst(exp, "[\\.\\$]");
160: exp = packageBoundary.subst(exp, "\\.");
161: exp = innerClassBoundary.subst(exp, "\\$");
162: exp = star.subst(exp, "@");
163: exp = matchAcross.subst(exp, ".*");
164: exp = matchWithin.subst(exp, "[^\\.]*");
165: // exp = matchWithinInner.subst(exp, "[^\\.\\$]*");
166: return exp;
167: }
168:
169: private static void buildStaticPatterns() {
170: if (allowable == null)
171: try {
172: star = new RE("\\*");
173: matchWithin = new RE("@");
174: matchAcross = new RE("@@");
175: partBoundary = new RE("\\.");
176: packageBoundary = new RE("/");
177: innerClassBoundary = new RE("\\$");
178:
179: String varS = "\\$\\{([A-Za-z0-9_\\.\\-]+)\\}";
180: String partS = "(([A-Za-z_]|[\\(\\)]|\\*|" + varS + ")"
181: + "([A-Za-z0-9_]|[\\(\\)]|\\*|" + varS + ")*)";
182: var = new RE(varS);
183: allowable = new RE("^([\\$\\./]?" + partS + ")+$",
184: RE.MATCH_SINGLELINE);
185: allowableNoParts = new RE("^" + partS + "$",
186: RE.MATCH_SINGLELINE);
187: } catch (RESyntaxException rese) {
188: rese.printStackTrace(System.out);
189: throw new RuntimeException(
190: "Can't initialize RegexPattern: " + rese);
191: }
192: }
193:
194: private RE regex;
195: private List/*<Part>*/parts;
196: private Map prevVarValues, matchCache, matchResultCache;
197: static private RE star, matchWithin, matchAcross, partBoundary,
198: packageBoundary, innerClassBoundary, var, allowable,
199: allowableNoParts;
200:
201: private class Part {
202: }
203:
204: private class VarPart extends Part {
205: public VarPart(String varName) {
206: this .varName = varName;
207: }
208:
209: public String varName;
210:
211: public String toString() {
212: return "var(" + varName + ")";
213: }
214: }
215:
216: private class ExpPart extends Part {
217: public ExpPart(String exp) {
218: this .exp = exp;
219: }
220:
221: public String exp;
222:
223: public String toString() {
224: return "exp(" + exp + ")";
225: }
226: }
227:
228: //--------------------------------------------------------------------------
229: // Object
230: //--------------------------------------------------------------------------
231:
232: public String toString() {
233: return '"' + regexStr + '"';
234: }
235: }
|