All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.hibernate.hql.testing.internal.parser.GrammarRuleTestRunner Maven / Gradle / Ivy

/*
 * Hibernate, Relational Persistence for Idiomatic Java
 *
 * JBoss, Home of Professional Open Source
 * Copyright 2013 Red Hat Inc. and/or its affiliates and other contributors
 * as indicated by the @authors tag. All rights reserved.
 * See the copyright.txt in the distribution for a
 * full listing of individual contributors.
 *
 * This copyrighted material is made available to anyone wishing to use,
 * modify, copy, or redistribute it subject to the terms and conditions
 * of the GNU Lesser General Public License, v. 2.1.
 * This program is distributed in the hope that it will be useful, but WITHOUT A
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
 * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
 * You should have received a copy of the GNU Lesser General Public License,
 * v.2.1 along with this distribution; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA  02110-1301, USA.
 */
package org.hibernate.hql.testing.internal.parser;

import java.io.ByteArrayOutputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;

import org.antlr.runtime.ANTLRStringStream;
import org.antlr.runtime.CharStream;
import org.antlr.runtime.CommonTokenStream;
import org.antlr.runtime.Lexer;
import org.antlr.runtime.Parser;
import org.antlr.runtime.RecognitionException;
import org.antlr.runtime.RuleReturnScope;
import org.antlr.runtime.Token;
import org.antlr.runtime.TokenStream;
import org.antlr.runtime.tree.CommonTree;
import org.hibernate.hql.testing.internal.model.GrammarRuleTestDescriptor;
import org.hibernate.hql.testing.internal.model.GrammarRuleTestGroupDescriptor;
import org.hibernate.hql.testing.internal.model.GrammarTestDescriptor;
import org.hibernate.hql.testing.internal.model.ParsingResult;
import org.hibernate.hql.testing.internal.model.RuleType;

/**
 * Runs a single {@link GrammarRuleTestDescriptor grammar rule test}.
 *
 * @author Gunnar Morling
 */
public class GrammarRuleTestRunner {

	private final GrammarTestDescriptor grammarTest;
	private final GrammarRuleTestGroupDescriptor grammarRuleTestGroup;
	private final GrammarRuleTestDescriptor grammarRuleTest;

	public GrammarRuleTestRunner(
			GrammarTestDescriptor grammarTest,
			GrammarRuleTestGroupDescriptor grammarRuleTestGroup,
			GrammarRuleTestDescriptor grammarRuleTest) {

		this.grammarTest = grammarTest;
		this.grammarRuleTestGroup = grammarRuleTestGroup;
		this.grammarRuleTest = grammarRuleTest;
	}

	/**
	 * Runs the given grammar rule test by creating lexer and parser for the
	 * grammar under test and invoking the appropriate methods on them.
	 *
	 * @return a descriptor representing the outcoming of this operation
	 *
	 * @throws Throwable in case of any errors
	 */
	public ParsingResult run() throws Throwable {
		Lexer lexer = getLexer();
		ParsingResult parsingResult;

		if ( grammarRuleTestGroup.getRuleType() == RuleType.LEXER ) {
			Method rule = lexer.getClass().getMethod( "m" + grammarRuleTestGroup.getName() );

			parsingResult = invokeRule( lexer, rule );
		}
		else {
			CommonTokenStream tokens = new CommonTokenStream( lexer );
			Parser parser = getParser( tokens );
			Method rule = parser.getClass().getMethod( grammarRuleTestGroup.getName() );

			parsingResult = invokeRule( parser, rule );

			//consider unconsumed tokens as FAIL
			String unconsumedTokens = getUnconsumedTokens( tokens );
			if ( unconsumedTokens != null ) {
				parsingResult = ParsingResult.fail( "Found unconsumed tokens: \"" + unconsumedTokens + "\"." );
			}
		}

		return parsingResult;
	}

	/**
	 * Invokes the given lexer or parser rule on the given target object.
	 *
	 * @param target the lexer or parser to invoke the given method on
	 * @param rule the rule to invoke
	 *
	 * @return the outcome of the lexer or parser rule. The status will be
	 *         {@link ParsingResult.Status#FAIL} in case anything is written to
	 *         {@link System#err} during parsing or in case a
	 *         {@link RecognitionException} is thrown, otherwise
	 *         {@link ParsingResult.Status#OK}.
	 *
	 * @throws Throwable In case of any unexpected error.
	 */
	private ParsingResult invokeRule(Object target, Method rule) throws Throwable {
		PrintStream originalErrorStream = System.err;

		try {
			// have to catch any output written to System.err in Antlr3 since
			// there is no way to register an error handler
			ByteArrayOutputStream errorStream = new ByteArrayOutputStream();
			System.setErr( new PrintStream( errorStream ) );

			Object result = rule.invoke( target );
			String ast = getAst( result );

			return getParsingResult( errorStream, ast );
		}
		catch (InvocationTargetException ite) {
			try {
				if ( ite.getCause() != null ) {
					throw ite.getCause();
				}
				else {
					throw ite;
				}
			}
			catch (RecognitionException re) {
				return ParsingResult.fail( re.getMessage() );
			}
		}
		finally {
			System.setErr( originalErrorStream );
		}
	}

	private ParsingResult getParsingResult(OutputStream errorStream, String ast) {
		return errorStream.toString().isEmpty() ? ParsingResult.ok( ast ) : ParsingResult.fail(
				errorStream.toString(),
				ast
		);
	}

	private String getAst(Object result) {
		if ( result instanceof RuleReturnScope ) {
			return ((CommonTree) ((RuleReturnScope) result).getTree()).toStringTree();
		}

		return null;
	}

	private Lexer getLexer() throws Exception {
		return grammarTest
				.getLexerClass()
				.getConstructor( CharStream.class )
				.newInstance( new ANTLRStringStream( grammarRuleTest.getExpression() ) );
	}

	private Parser getParser(CommonTokenStream tokens) throws Exception {
		return grammarTest
				.getParserClass()
				.getConstructor( TokenStream.class )
				.newInstance( tokens );
	}

	private String getUnconsumedTokens(CommonTokenStream tokens) {
		// ensure we've buffered all tokens from the underlying TokenSource
		tokens.fill();
		if ( tokens.index() == tokens.size() - 1 ) {
			return null;
		}

		StringBuilder nonEofEndingTokens = new StringBuilder();

		for ( Token endToken : (List) tokens.getTokens( tokens.index(), tokens.size() - 1 ) ) {
			// Ignore  tokens as they might be inserted by the parser
			if ( endToken.getType() != Token.EOF ) {
				nonEofEndingTokens.append( endToken.getText() );
			}
		}

		return nonEofEndingTokens.length() > 0 ? nonEofEndingTokens.toString() : null;
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy