/*
* Copyright 2010 Red Hat, Inc. and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.drools.core.reteoo.test.parser;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Stack;
import java.util.Map.Entry;
import org.antlr.runtime.EarlyExitException;
import org.antlr.runtime.FailedPredicateException;
import org.antlr.runtime.MismatchedNotSetException;
import org.antlr.runtime.MismatchedSetException;
import org.antlr.runtime.MismatchedTokenException;
import org.antlr.runtime.MismatchedTreeNodeException;
import org.antlr.runtime.NoViableAltException;
import org.antlr.runtime.RecognitionException;
import org.antlr.runtime.Token;
/**
* Helper class that generates DroolsParserException with user friendly error
* messages.
*
* @see DroolsParserException
*/
public class DroolsParserExceptionFactory {
public final static String MISMATCHED_TOKEN_MESSAGE_COMPLETE = "Line %1$d:%2$d mismatched input '%3$s' expecting '%4$s'%5$s";
public final static String MISMATCHED_TOKEN_MESSAGE_PART = "Line %1$d:%2$d mismatched input '%3$s'%4$s";
public final static String MISMATCHED_TREE_NODE_MESSAGE_COMPLETE = "Line %1$d:%2$d mismatched tree node '%3$s' expecting '%4$s'%5$s";
public final static String MISMATCHED_TREE_NODE_MESSAGE_PART = "Line %1$d:%2$d mismatched tree node '%3$s'%4$s";
public final static String NO_VIABLE_ALT_MESSAGE = "Line %1$d:%2$d no viable alternative at input '%3$s'%4$s";
public final static String EARLY_EXIT_MESSAGE = "Line %1$d:%2$d required (...)+ loop did not match anything at input '%3$s'%4$s";
public final static String MISMATCHED_SET_MESSAGE = "Line %1$d:%2$d mismatched input '%3$' expecting set '%4$s'%5$s.";
public final static String MISMATCHED_NOT_SET_MESSAGE = "Line %1$d:%2$d mismatched input '%3$' expecting set '%4$s'%5$s";
public final static String FAILED_PREDICATE_MESSAGE = "Line %1$d:%2$d rule '%3$s' failed predicate: {%4$s}?%5$s";
public final static String TRAILING_SEMI_COLON_NOT_ALLOWED_MESSAGE = "Line %1$d:%2$d trailing semi-colon not allowed%3$s";
public final static String PARSER_LOCATION_MESSAGE_COMPLETE = " in %1$s %2$s";
public final static String PARSER_LOCATION_MESSAGE_PART = " in %1$s";
private String[] tokenNames = null;
private Stack<Map<DroolsParaphraseTypes, String>> paraphrases = null;
/**
* DroolsParserErrorMessages constructor.
*
* @param tokenNames
* tokenNames generated by ANTLR
* @param paraphrases
* paraphrases parser structure
*/
public DroolsParserExceptionFactory(String[] tokenNames,
Stack<Map<DroolsParaphraseTypes, String>> paraphrases) {
this.tokenNames = tokenNames;
this.paraphrases = paraphrases;
}
/**
* This method creates a DroolsParserException for trailing semicolon
* exception, full of information.
*
* @param line
* line number
* @param column
* column position
* @param offset
* char offset
* @return DroolsParserException filled.
*/
public DroolsParserException createTrailingSemicolonException(int line,
int column, int offset) {
String message = String
.format(
DroolsParserExceptionFactory.TRAILING_SEMI_COLON_NOT_ALLOWED_MESSAGE,
line, column, formatParserLocation());
return new DroolsParserException("ERR 104", message, line, column,
offset, null);
}
/**
* This method creates a DroolsParserException full of information.
*
* @param e
* original exception
* @return DroolsParserException filled.
*/
public DroolsParserException createDroolsException(RecognitionException e) {
List<String> codeAndMessage = createErrorMessage(e);
return new DroolsParserException(codeAndMessage.get(1), codeAndMessage
.get(0), e.line, e.charPositionInLine, e.index, e);
}
/**
* This will take a RecognitionException, and create a sensible error
* message out of it
*/
private List<String> createErrorMessage(RecognitionException e) {
List<String> codeAndMessage = new ArrayList<String>(2);
String message = "";
if (e instanceof MismatchedTokenException) {
MismatchedTokenException mte = (MismatchedTokenException) e;
if (tokenNames != null && mte.expecting >= 0 && mte.expecting < tokenNames.length) {
message = String
.format(
DroolsParserExceptionFactory.MISMATCHED_TOKEN_MESSAGE_COMPLETE,
e.line, e.charPositionInLine,
getBetterToken(e.token),
getBetterToken(mte.expecting),
formatParserLocation());
codeAndMessage.add(message);
codeAndMessage.add("ERR 102");
} else {
message = String
.format(
DroolsParserExceptionFactory.MISMATCHED_TOKEN_MESSAGE_PART,
e.line, e.charPositionInLine,
getBetterToken(e.token), formatParserLocation());
codeAndMessage.add(message);
codeAndMessage.add("ERR 102");
}
} else if (e instanceof MismatchedTreeNodeException) {
MismatchedTreeNodeException mtne = (MismatchedTreeNodeException) e;
if (mtne.expecting >= 0 && mtne.expecting < tokenNames.length) {
message = String
.format(
DroolsParserExceptionFactory.MISMATCHED_TREE_NODE_MESSAGE_COMPLETE,
e.line, e.charPositionInLine,
getBetterToken(e.token),
getBetterToken(mtne.expecting),
formatParserLocation());
codeAndMessage.add(message);
codeAndMessage.add("ERR 106");
} else {
message = String
.format(
DroolsParserExceptionFactory.MISMATCHED_TREE_NODE_MESSAGE_PART,
e.line, e.charPositionInLine,
getBetterToken(e.token), formatParserLocation());
codeAndMessage.add(message);
codeAndMessage.add("ERR 106");
}
} else if (e instanceof NoViableAltException) {
// NoViableAltException nvae = (NoViableAltException) e;
message = String.format(
DroolsParserExceptionFactory.NO_VIABLE_ALT_MESSAGE, e.line,
e.charPositionInLine, getBetterToken(e.token),
formatParserLocation());
codeAndMessage.add(message);
codeAndMessage.add("ERR 101");
} else if (e instanceof EarlyExitException) {
// EarlyExitException eee = (EarlyExitException) e;
message = String.format(
DroolsParserExceptionFactory.EARLY_EXIT_MESSAGE, e.line,
e.charPositionInLine, getBetterToken(e.token),
formatParserLocation());
codeAndMessage.add(message);
codeAndMessage.add("ERR 105");
} else if (e instanceof MismatchedSetException) {
MismatchedSetException mse = (MismatchedSetException) e;
message = String.format(
DroolsParserExceptionFactory.MISMATCHED_SET_MESSAGE,
e.line, e.charPositionInLine, getBetterToken(e.token),
mse.expecting, formatParserLocation());
codeAndMessage.add(message);
codeAndMessage.add("ERR 107");
} else if (e instanceof MismatchedNotSetException) {
MismatchedNotSetException mse = (MismatchedNotSetException) e;
message = String.format(
DroolsParserExceptionFactory.MISMATCHED_NOT_SET_MESSAGE,
e.line, e.charPositionInLine, getBetterToken(e.token),
mse.expecting, formatParserLocation());
codeAndMessage.add(message);
codeAndMessage.add("ERR 108");
} else if (e instanceof FailedPredicateException) {
FailedPredicateException fpe = (FailedPredicateException) e;
message = String.format(
DroolsParserExceptionFactory.FAILED_PREDICATE_MESSAGE,
e.line, e.charPositionInLine, fpe.ruleName,
fpe.predicateText, formatParserLocation());
codeAndMessage.add(message);
codeAndMessage.add("ERR 103");
}
if (codeAndMessage.get(0).length() == 0) {
codeAndMessage.add("?????");
}
return codeAndMessage;
}
/**
* This will take Paraphrases stack, and create a sensible location
*/
private String formatParserLocation() {
StringBuilder sb = new StringBuilder();
if (paraphrases != null){
for (Map<DroolsParaphraseTypes, String> map : paraphrases) {
for (Entry<DroolsParaphraseTypes, String> activeEntry : map
.entrySet()) {
if (activeEntry.getValue().length() == 0) {
sb.append(String.format(PARSER_LOCATION_MESSAGE_PART,
getLocationName(activeEntry.getKey())));
} else {
sb.append(String.format(PARSER_LOCATION_MESSAGE_COMPLETE,
getLocationName(activeEntry.getKey()), activeEntry
.getValue()));
}
}
}
}
return sb.toString();
}
/**
* Returns a string based on Paraphrase Type
*
* @param type
* Paraphrase Type
* @return a string representing the
*/
private String getLocationName(DroolsParaphraseTypes type) {
switch (type) {
case TESTCASE:
return "TestCase";
case SETUP:
return "Setup";
case TEARDOWN:
return "TearDown";
case TEST:
return "Test";
case STEP:
return "Step";
case COMMAND:
return "Command";
default:
return "";
}
}
/**
* Helper method that creates a user friendly token definition
*
* @param token
* token
* @return user friendly token definition
*/
private String getBetterToken(Token token) {
if (token == null){
return "";
}
return getBetterToken(token.getType(), token.getText());
}
/**
* Helper method that creates a user friendly token definition
*
* @param tokenType
* token type
* @return user friendly token definition
*/
private String getBetterToken(int tokenType) {
return getBetterToken(tokenType, null);
}
/**
* Helper method that creates a user friendly token definition
*
* @param tokenType
* token type
* @param defaultValue
* default value for identifier token, may be null
* @return user friendly token definition
*/
private String getBetterToken(int tokenType, String defaultValue) {
switch (tokenType) {
default:
return ( tokenType < 0 || tokenType > tokenNames.length ) ? "<EOF>"
: tokenNames[tokenType];
}
}
}