parser.java
来自「国外的一套开源CRM」· Java 代码 · 共 359 行
JAVA
359 行
/*
* $Id: Parser.java,v 1.1 2003/08/19 01:12:57 jonesde Exp $
*
* Copyright (c) 1999 Steven J. Metsker.
* Copyright (c) 2001 The Open For Business Project - www.ofbiz.org
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
* OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
* THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package org.ofbiz.rules.parse;
import java.util.*;
/**
* A <code>Parser</code> is an object that recognizes the elements of a language.
* <p>
* Each <code>Parser</code> object is either a <code>
* Terminal</code> or a composition of other parsers.
* The <code>Terminal</code> class is a subclass of <code>
* Parser</code>, and is itself a hierarchy of
* parsers that recognize specific patterns of text. For
* example, a <code>Word</code> recognizes any word, and a
* <code>Literal</code> matches a specific string.
* <p>
* In addition to <code>Terminal</code>, other subclasses of
* <code>Parser</code> provide composite parsers,
* describing sequences, alternations, and repetitions of
* other parsers. For example, the following <code>
* Parser</code> objects culminate in a <code>good
* </code> parser that recognizes a description of good
* coffee.
*
* <blockquote><pre>
* Alternation adjective = new Alternation();
* adjective.add(new Literal("steaming"));
* adjective.add(new Literal("hot"));
* Sequence good = new Sequence();
* good.add(new Repetition(adjective));
* good.add(new Literal("coffee"));
* String s = "hot hot steaming hot coffee";
* Assembly a = new TokenAssembly(s);
* System.out.println(good.bestMatch(a));
* </pre></blockquote>
*
* This prints out:
*
* <blockquote><pre>
* [hot, hot, steaming, hot, coffee]
* hot/hot/steaming/hot/coffee^
* </pre></blockquote>
*
* The parser does not match directly against a string,
* it matches against an <code>Assembly</code>. The
* resulting assembly shows its stack, with four words on it,
* along with its sequence of tokens, and the index at the
* end of these. In practice, parsers will do some work
* on an assembly, based on the text they recognize.
*
* @author Steven J. Metsker
* @version 1.0
*/
public abstract class Parser {
/** a name to identify this parser */
protected String name;
/**
* an object that will work on an assembly whenever this
* parser successfully matches against the assembly
*/
protected Assembler assembler;
/** Constructs a nameless parser. */
public Parser() {}
/**
* Constructs a parser with the given name.
*
* @param name A name to be known by. For parsers
* that are deep composites, a simple name
* identifying its purpose is useful.
*/
public Parser(String name) {
this.name = name;
}
/**
* Accepts a "visitor" which will perform some operation on
* a parser structure. The book, "Design Patterns", explains
* the visitor pattern.
*
* @param pv the visitor to accept
*/
public void accept(ParserVisitor pv) {
accept(pv, new ArrayList());
}
/**
* Accepts a "visitor" along with a collection of previously
* visited parsers.
*
* @param pv the visitor to accept
* @param visited a collection of previously visited
* parsers.
*/
public abstract void accept(ParserVisitor pv, List visited);
/**
* Adds the elements of one vector to another.
*
* @param v1 the vector to add to
* @param v2 the vector with elements to add
*/
public static void add(List v1, List v2) {
Enumeration e = Collections.enumeration(v2);
while (e.hasMoreElements()) {
v1.add(e.nextElement());
}
}
/**
* Returns the most-matched assembly in a collection.
*
* @return the most-matched assembly in a collection.
* @param v the collection to look through
*
*/
public Assembly best(List v) {
Assembly best = null;
Enumeration e = Collections.enumeration(v);
while (e.hasMoreElements()) {
Assembly a = (Assembly) e.nextElement();
if (!a.hasMoreElements()) {
return a;
}
if (best == null) {
best = a;
} else {
if (a.elementsConsumed() > best.elementsConsumed()) {
best = a;
}
}
}
return best;
}
/**
* Returns an assembly with the greatest possible number of
* elements consumed by matches of this parser.
*
* @return an assembly with the greatest possible number of
* elements consumed by this parser
* @param a an assembly to match against
*/
public Assembly bestMatch(Assembly a) {
List in = new ArrayList();
in.add(a);
List out = matchAndAssemble(in);
// if (Debug.verboseOn()) Debug.logVerbose("[bestMatch] after match and assemble, before best: in=" + in + ", out=" + out, module);
return best(out);
}
/**
* Returns either null, or a completely matched version of
* the supplied assembly.
*
* @return either null, or a completely matched version of the
* supplied assembly
* @param a an assembly to match against
*/
public Assembly completeMatch(Assembly a) {
Assembly best = bestMatch(a);
if (best != null && !best.hasMoreElements()) {
return best;
}
return null;
}
/**
* Create a copy of a vector, cloning each element of
* the vector.
*
* @param in the vector to copy
* @return a copy of the input vector, cloning each
* element of the vector
*/
public static List elementClone(List v) {
List copy = new ArrayList();
Enumeration e = Collections.enumeration(v);
while (e.hasMoreElements()) {
Assembly a = (Assembly) e.nextElement();
copy.add(a.clone());
}
return copy;
}
/**
* Returns the name of this parser.
*
* @return the name of this parser
*/
public String getName() {
return name;
}
/**
* Given a set (well, a <code>List</code>, really) of
* assemblies, this method matches this parser against
* all of them, and returns a new set (also really a
* <code>List</code>) of the assemblies that result from
* the matches.
* <p>
* For example, consider matching the regular expression
* <code>a*</code> against the string <code>"aaab"</code>.
* The initial set of states is <code>{^aaab}</code>, where
* the ^ indicates how far along the assembly is. When
* <code>a*</code> matches against this initial state, it
* creates a new set <code>{^aaab, a^aab, aa^ab,
* aaa^b}</code>.
*
* @return a List of assemblies that result from
* matching against a beginning set of assemblies
* @param in a vector of assemblies to match against
*/
public abstract List match(List in);
/**
* Match this parser against an input state, and then
* apply this parser's assembler against the resulting
* state.
*
* @return a List of assemblies that result from matching
* against a beginning set of assemblies
* @param in a vector of assemblies to match against
*/
public List matchAndAssemble(List in) {
List out = match(in);
// if (Debug.verboseOn()) Debug.logVerbose("[matchAndAssemble] after match, before assemble: in=" + in + ", out=" + out, module);
if (assembler != null) {
Enumeration e = Collections.enumeration(out);
while (e.hasMoreElements()) {
assembler.workOn((Assembly) e.nextElement());
}
}
return out;
}
/**
* Create a random expansion for this parser, where a
* concatenation of the returned collection will be a
* language element.
*/
protected abstract List randomExpansion(int maxDepth, int depth);
/**
* Return a random element of this parser's language.
*
* @return a random element of this parser's language
*/
public String randomInput(int maxDepth, String separator) {
StringBuffer buf = new StringBuffer();
Enumeration e = Collections.enumeration(randomExpansion(maxDepth, 0));
boolean first = true;
while (e.hasMoreElements()) {
if (!first) {
buf.append(separator);
}
buf.append(e.nextElement());
first = false;
}
return buf.toString();
}
/**
* Sets the object that will work on an assembly whenever
* this parser successfully matches against the
* assembly.
*
* @param Assembler the assembler to apply
*
* @return Parser this
*/
public Parser setAssembler(Assembler assembler) {
this.assembler = assembler;
return this;
}
/**
* Returns a textual description of this parser.
*
* @return String a textual description of this
* parser, taking care to avoid
* infinite recursion
*/
public String toString() {
return toString(new ArrayList());
}
/**
* Returns a textual description of this parser.
* Parsers can be recursive, so when building a
* descriptive string, it is important to avoid infinite
* recursion by keeping track of the objects already
* described. This method keeps an object from printing
* twice, and uses <code>unvisitedString</code> which
* subclasses must implement.
*
* @param visited a list of objects already printed
*
* @return a textual version of this parser,
* avoiding recursion
*/
protected String toString(List visited) {
if (name != null) {
return name;
} else if (visited.contains(this)) {
return "...";
} else {
visited.add(this);
return unvisitedString(visited);
}
}
/**
* Returns a textual description of this string.
*/
protected abstract String unvisitedString(List visited);
}
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?