Skip to content

Commit

Permalink
MODE-1986 Enhance DdlParsers to provide access to parser 'scoring'
Browse files Browse the repository at this point in the history
Added the recommended changes to the DdlParsers API. One slight change was that the result "getScore" method returns and "int" and not an "Object."
  • Loading branch information
elvisisking authored and rhauch committed Jul 5, 2013
1 parent 18c8675 commit 75dbaa8
Show file tree
Hide file tree
Showing 6 changed files with 302 additions and 25 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,11 @@
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.modeshape.common.annotation.Immutable;
import org.modeshape.common.text.ParsingException;
import org.modeshape.common.text.Position;
Expand Down Expand Up @@ -69,13 +71,13 @@ public int compare( final Entry<DdlParser, Integer> thisEntry,

// default to standard SQL parser if score is a tie
if (result == 0) {
if (StandardDdlParser.PARSE_ID.equals(thisEntry.getKey().getId())
&& !StandardDdlParser.PARSE_ID.equals(thatEntry.getKey().getId())) {
if (StandardDdlParser.ID.equals(thisEntry.getKey().getId())
&& !StandardDdlParser.ID.equals(thatEntry.getKey().getId())) {
return -1;
}

if (StandardDdlParser.PARSE_ID.equals(thatEntry.getKey().getId())
&& !StandardDdlParser.PARSE_ID.equals(thisEntry.getKey().getId())) {
if (StandardDdlParser.ID.equals(thatEntry.getKey().getId())
&& !StandardDdlParser.ID.equals(thisEntry.getKey().getId())) {
return 1;
}
}
Expand Down Expand Up @@ -116,6 +118,36 @@ public DdlParsers( List<DdlParser> parsers ) {
this.parsers = (parsers != null && !parsers.isEmpty()) ? parsers : BUILTIN_PARSERS;
}

private AstNode createDdlStatementsContainer( final String parserId ) {
final AstNode node = this.nodeFactory.node(StandardDdlLexicon.STATEMENTS_CONTAINER);
node.setProperty(JcrConstants.JCR_PRIMARY_TYPE, JcrConstants.NT_UNSTRUCTURED);
node.setProperty(StandardDdlLexicon.PARSER_ID, parserId);
return node;
}

/**
* @param id the identifier of the parser being requested (cannot be <code>null</code> or empty)
* @return the parser or <code>null</code> if not found
*/
public DdlParser getParser( final String id ) {
CheckArg.isNotEmpty(id, "id");

for (final DdlParser parser : this.parsers) {
if (parser.getId().equals(id)) {
return parser;
}
}

return null;
}

/**
* @return a copy of the DDL parsers used in this instance (never <code>null</code> or empty)
*/
public Set<DdlParser> getParsers() {
return new HashSet<DdlParser>(this.parsers);
}

/**
* @param ddl the DDL being parsed (cannot be <code>null</code> or empty)
* @param parserId the identifier of the parser to use (can be <code>null</code> or empty if best matched parser should be
Expand All @@ -129,31 +161,134 @@ public AstNode parseUsing( final String ddl,
CheckArg.isNotEmpty(ddl, "ddl");
CheckArg.isNotEmpty(parserId, "parserId");

DdlParser parser = null;

for (final DdlParser ddlParser : this.parsers) {
if (parserId.equals(ddlParser.getId())) {
parser = ddlParser;
break;
}
}
DdlParser parser = getParser(parserId);

if (parser == null) {
throw new ParsingException(Position.EMPTY_CONTENT_POSITION,
DdlSequencerI18n.unknownParser.text(parserId));
throw new ParsingException(Position.EMPTY_CONTENT_POSITION, DdlSequencerI18n.unknownParser.text(parserId));
}

// create DDL root node
AstNode astRoot = this.nodeFactory.node(StandardDdlLexicon.STATEMENTS_CONTAINER);
astRoot.setProperty(JcrConstants.JCR_PRIMARY_TYPE, JcrConstants.NT_UNSTRUCTURED);
astRoot.setProperty(StandardDdlLexicon.PARSER_ID, parserId);
AstNode astRoot = createDdlStatementsContainer(parserId);

// parse
parser.parse(ddl, astRoot, null);

return astRoot;
}

/**
* Parse the supplied DDL using multiple parsers, returning the result of each parser with its score in the order of highest
* scoring to lowest scoring.
*
* @param ddl the DDL being parsed (cannot be <code>null</code> or empty)
* @param firstParserId the identifier of the first parser to use (cannot be <code>null</code> or empty)
* @param secondParserId the identifier of the second parser to use (cannot be <code>null</code> or empty)
* @param additionalParserIds the identifiers of additional parsers that should be used; may be empty but not contain a null
* identifier value
* @return the list of {@link ParsingResult} instances, one for each parser, ordered from highest score to lowest score (never
* <code>null</code> or empty)
* @throws ParsingException if there is an error parsing the supplied DDL content
* @throws IllegalArgumentException if a parser with the specified identifier cannot be found
*/
public List<ParsingResult> parseUsing( final String ddl,
final String firstParserId,
final String secondParserId,
final String... additionalParserIds ) throws ParsingException {
CheckArg.isNotEmpty(firstParserId, "firstParserId");
CheckArg.isNotEmpty(secondParserId, "secondParserId");

if (additionalParserIds != null) {
CheckArg.containsNoNulls(additionalParserIds, "additionalParserIds");
}

final int numParsers = ((additionalParserIds == null) ? 2 : (additionalParserIds.length + 2));
final List<DdlParser> selectedParsers = new ArrayList<DdlParser>(numParsers);

{ // add first parser
final DdlParser parser = getParser(firstParserId);

if (parser == null) {
throw new ParsingException(Position.EMPTY_CONTENT_POSITION, DdlSequencerI18n.unknownParser.text(firstParserId));
}

selectedParsers.add(parser);
}

{ // add second parser
final DdlParser parser = getParser(secondParserId);

if (parser == null) {
throw new ParsingException(Position.EMPTY_CONTENT_POSITION, DdlSequencerI18n.unknownParser.text(secondParserId));
}

selectedParsers.add(parser);
}

// add remaining parsers
if ((additionalParserIds != null) && (additionalParserIds.length != 0)) {
for (final String id : additionalParserIds) {
final DdlParser parser = getParser(id);

if (parser == null) {
throw new ParsingException(Position.EMPTY_CONTENT_POSITION, DdlSequencerI18n.unknownParser.text(id));
}

selectedParsers.add(parser);
}
}

return parseUsing(ddl, selectedParsers);
}

private List<ParsingResult> parseUsing( final String ddl,
final List<DdlParser> parsers ) {
CheckArg.isNotEmpty(ddl, "ddl");

final List<ParsingResult> results = new ArrayList<DdlParsers.ParsingResult>(this.parsers.size());
final DdlParserScorer scorer = new DdlParserScorer();

for (final DdlParser parser : this.parsers) {
final String parserId = parser.getId();
int score = ParsingResult.NO_SCORE;
AstNode rootNode = null;
Exception error = null;

try {
// score
final Object scorerOutput = parser.score(ddl, null, scorer);
score = scorer.getScore();

// create DDL root node
rootNode = createDdlStatementsContainer(parserId);

// parse
parser.parse(ddl, rootNode, scorerOutput);
} catch (final RuntimeException e) {
error = e;
} finally {
final ParsingResult result = new ParsingResult(parserId, rootNode, score, error);
results.add(result);
scorer.reset();
}
}

Collections.sort(results);
return results;
}

/**
* Parse the supplied DDL using all registered parsers, returning the result of each parser with its score in the order of
* highest scoring to lowest scoring.
*
* @param ddl the DDL being parsed (cannot be <code>null</code> or empty)
* @return the list or {@link ParsingResult} instances, one for each parser, ordered from highest score to lowest score
* @throws ParsingException if there is an error parsing the supplied DDL content
* @throws IllegalArgumentException if a parser with the specified identifier cannot be found
*/
public List<ParsingResult> parseUsingAll( final String ddl ) throws ParsingException {
return parseUsing(ddl, this.parsers);
}

/**
* Parse the supplied DDL content and return the {@link AstNode root node} of the AST representation.
*
Expand Down Expand Up @@ -205,9 +340,7 @@ public AstNode parse( final String ddl,
final DdlParser parser = scoredParser.getKey();

// create DDL root node
astRoot = this.nodeFactory.node(StandardDdlLexicon.STATEMENTS_CONTAINER);
astRoot.setProperty(JcrConstants.JCR_PRIMARY_TYPE, JcrConstants.NT_UNSTRUCTURED);
astRoot.setProperty(StandardDdlLexicon.PARSER_ID, parser.getId());
astRoot = createDdlStatementsContainer(parser.getId());

// parse
parser.parse(ddl, astRoot, null);
Expand All @@ -229,4 +362,80 @@ public AstNode parse( final String ddl,

throw firstException;
}

/**
* Represents a parsing result of one parser parsing one DDL input.
*/
@Immutable
public class ParsingResult implements Comparable<ParsingResult> {

public static final int NO_SCORE = -1;

private final Exception error;
private final String id;
private final AstNode rootNode;
private final int score;

/**
* @param parserId the parser identifier (cannot be <code>null</code> or empty)
* @param rootTreeNode the node at the root of the parse tree (can be <code>null</code> if an error occurred)
* @param parserScore the parsing score (can have {@link #NO_SCORE no score} if an error occurred
* @param parsingError an error that occurred during parsing (can be <code>null</code>)
*/
public ParsingResult( final String parserId,
final AstNode rootTreeNode,
final int parserScore,
final Exception parsingError ) {
CheckArg.isNotEmpty(parserId, "parserId");

this.id = parserId;
this.rootNode = rootTreeNode;
this.score = parserScore;
this.error = parsingError;
}

/**
* {@inheritDoc}
*
* @see java.lang.Comparable#compareTo(java.lang.Object)
*/
@Override
public int compareTo( final ParsingResult that ) {
if ((this == that) || (this.score == that.score)) {
return 0;
}

return ((this.score > that.score) ? -1 : 1);
}

/**
* @return the parsing error (<code>null</code> if no error occurred)
*/
public Exception getError() {
return this.error;
}

/**
* @return the parser identifier (never <code>null</code> or empty)
*/
public String getParserId() {
return this.id;
}

/**
* @return the root <code>AstNode</code> (can be <code>null</code> if a parsing error occurred)
*/
public AstNode getRootTree() {
return this.rootNode;
}

/**
* @return the parsing score
*/
public int getScore() {
return this.score;
}

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ public class StandardDdlParser implements DdlParser, DdlConstants, DdlConstants.
/**
* The Standard DDL parser identifier.
*/
public static final String PARSE_ID = "SQL92";
public static final String ID = "SQL92";

private boolean testMode = false;
private final List<DdlParserProblem> problems;
Expand Down Expand Up @@ -2874,7 +2874,7 @@ public void setTestMode( boolean testMode ) {
*/
@Override
public String getId() {
return PARSE_ID;
return ID;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ public class DerbyDdlParser extends StandardDdlParser implements DerbyDdlConstan
/**
* The Derby parser identifier.
*/
private static final String ID = "DERBY";
public static final String ID = "DERBY";

protected static final List<String[]> derbyDataTypeStrings = new ArrayList<String[]>(
DerbyDataTypes.CUSTOM_DATATYPE_START_PHRASES);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ public class MySqlDdlParser extends StandardDdlParser implements MySqlDdlConstan
/**
* The MySQL parser identifier.
*/
private static final String ID = "MYSQL";
public static final String ID = "MYSQL";

static List<String[]> mysqlDataTypeStrings = new ArrayList<String[]>();
/*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ public class OracleDdlParser extends StandardDdlParser
/**
* The Oracle parser identifier.
*/
private static final String ID = "ORACLE";
public static final String ID = "ORACLE";

static List<String[]> oracleDataTypeStrings = new ArrayList<String[]>();

Expand Down
Loading

0 comments on commit 75dbaa8

Please sign in to comment.