001: package org.ofbiz.rules.parse;
002:
003: import java.util.*;
004: import org.ofbiz.rules.utensil.*;
005:
006: /**
007: * <p><b>Title:</b> Parser Tester
008: * <p><b>Description:</b> None
009: * <p>Copyright (c) 1999 Steven J. Metsker.
010: * <p>Copyright (c) 2001 The Open For Business Project - www.ofbiz.org
011: *
012: * <p>Permission is hereby granted, free of charge, to any person obtaining a
013: * copy of this software and associated documentation files (the "Software"),
014: * to deal in the Software without restriction, including without limitation
015: * the rights to use, copy, modify, merge, publish, distribute, sublicense,
016: * and/or sell copies of the Software, and to permit persons to whom the
017: * Software is furnished to do so, subject to the following conditions:
018: *
019: * <p>The above copyright notice and this permission notice shall be included
020: * in all copies or substantial portions of the Software.
021: *
022: * <p>THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
023: * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
024: * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
025: * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
026: * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
027: * OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
028: * THE USE OR OTHER DEALINGS IN THE SOFTWARE.
029: *
030: * <br>
031: * <p>This class generates random language elements for a
032: * parser and tests that the parser can accept them.
033: *
034: * @author Steven J. Metsker
035: * @version 1.0
036: */
037: public abstract class ParserTester {
038: protected Parser p;
039: protected boolean logTestStrings = true;
040:
041: /**
042: * Constructs a tester for the given parser.
043: */
044: public ParserTester(Parser p) {
045: this .p = p;
046: }
047:
048: /**
049: * Subclasses must override this, to produce an assembly
050: * from the given (random) string.
051: */
052: protected abstract Assembly assembly(String s);
053:
054: /**
055: * Generate a random language element, and return true if
056: * the parser cannot unambiguously parse it.
057: */
058: protected boolean canGenerateProblem(int depth) {
059: String s = p.randomInput(depth, separator());
060:
061: logTestString(s);
062: Assembly a = assembly(s);
063:
064: a.setTarget(freshTarget());
065: List in = new ArrayList();
066:
067: in.add(a);
068: List out = completeMatches(p.match(in));
069:
070: if (out.size() != 1) {
071: logProblemFound(s, out.size());
072: return true;
073: }
074: return false;
075: }
076:
077: /**
078: * Return a subset of the supplied vector of assemblies,
079: * filtering for assemblies that have been completely
080: * matched.
081: *
082: * @param in a collection of partially or completely
083: * matched assemblies
084: *
085: * @return a collection of completely matched assemblies
086: */
087: public static List completeMatches(List in) {
088: List out = new ArrayList();
089: Enumeration e = Collections.enumeration(in);
090:
091: while (e.hasMoreElements()) {
092: Assembly a = (Assembly) e.nextElement();
093:
094: if (!a.hasMoreElements()) {
095: out.add(a);
096: }
097: }
098: return out;
099: }
100:
101: /**
102: * Give subclasses a chance to provide fresh target at
103: * the beginning of a parse.
104: */
105: protected PubliclyCloneable freshTarget() {
106: return null;
107: }
108:
109: /**
110: * This method is broken out to allow subclasses to create
111: * less verbose tester, or to direct logging to somewhere
112: * other than System.out.
113: */
114: protected void logDepthChange(int depth) {
115: System.out.println("Testing depth " + depth + "...");
116: }
117:
118: /**
119: * This method is broken out to allow subclasses to create
120: * less verbose tester, or to direct logging to somewhere
121: * other than System.out.
122: */
123: protected void logPassed() {
124: System.out.println("No problems found.");
125: }
126:
127: /**
128: * This method is broken out to allow subclasses to create
129: * less verbose tester, or to direct logging to somewhere
130: * other than System.out.
131: */
132: protected void logProblemFound(String s, int matchSize) {
133: System.out.println("Problem found for string:");
134: System.out.println(s);
135: if (matchSize == 0) {
136: System.out.println("Parser cannot match this apparently "
137: + "valid string.");
138: } else {
139: System.out.println("The parser found " + matchSize
140: + " ways to parse this string.");
141: }
142: }
143:
144: /**
145: * This method is broken out to allow subclasses to create
146: * less verbose tester, or to direct logging to somewhere
147: * other than System.out.
148: */
149: protected void logTestString(String s) {
150: if (logTestStrings) {
151: System.out.println(" Testing string " + s);
152: }
153: }
154:
155: /**
156: * By default, place a blank between randomly generated
157: * "words" of a language.
158: */
159: protected String separator() {
160: return " ";
161: }
162:
163: /**
164: * Set the boolean which determines if this class displays
165: * every test string.
166: *
167: * @param boolean true, if the user wants to see
168: * every test string
169: */
170: public void setLogTestStrings(boolean logTestStrings) {
171: this .logTestStrings = logTestStrings;
172: }
173:
174: /**
175: * Create a series of random language elements, and test
176: * that the parser can unambiguously parse each one.
177: */
178: public void test() {
179: for (int depth = 2; depth < 8; depth++) {
180: logDepthChange(depth);
181: for (int k = 0; k < 100; k++) {
182: if (canGenerateProblem(depth)) {
183: return;
184: }
185: }
186: }
187: logPassed();
188: }
189: }
|