/******************************************************************************* * Copyright (c) 2009, 2010 SAP AG and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * SAP AG - initial API and implementation ******************************************************************************/ package com.sap.furcas.runtime.parser.testbase; import static com.sap.furcas.test.util.CompilationHelper.getSourceRoot; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.PrintStream; import org.antlr.runtime.Lexer; import org.eclipse.emf.common.notify.Notifier; import org.eclipse.emf.ecore.resource.ResourceSet; import com.sap.furcas.parsergenerator.GrammarGenerationException; import com.sap.furcas.parsergenerator.TCSParserGenerator; import com.sap.furcas.parsergenerator.TCSParserGeneratorFactory; import com.sap.furcas.parsergenerator.TCSSyntaxContainerBean; import com.sap.furcas.runtime.common.exceptions.ParserGeneratorInvocationException; import com.sap.furcas.runtime.common.interfaces.IModelElementProxy; import com.sap.furcas.runtime.parser.ParserFacade; import com.sap.furcas.runtime.parser.exceptions.InvalidParserImplementationException; import com.sap.furcas.runtime.parser.impl.ObservableInjectingParser; import com.sap.furcas.runtime.referenceresolving.SyntaxRegistryFacade; import com.sap.furcas.runtime.tcs.RuleNameFinder; import com.sun.tools.javac.Main; /** * A class that allows to generate a language specific parser from a given TCS file. * Its methods must be called in the follwoing order: * * generateGrammar(), * generateParser(), * compileParser(), * loadParserFacade(). * * The class is configured (what to create, where to create it, ...) with the help * {@link GeneratedParserTestConfiguration}. It is used by the {@link GeneratedParserBasedTest}. * * @author Stephan Erb */ public class ParserGenerator { private final GeneratedParserTestConfiguration testConfig; public ParserGenerator(GeneratedParserTestConfiguration testConfig) { this.testConfig = testConfig; } public void generateGrammar(TCSSyntaxContainerBean syntaxBean) throws GrammarGenerationException { SystemOutErrorHandler errorHandler = new SystemOutErrorHandler(); try { TCSParserGenerator generator = TCSParserGeneratorFactory.INSTANCE.createTCSParserGenerator(); generator.generateGrammarFromSyntax(syntaxBean, testConfig.getSourceConfiguration(), testConfig.getTargetConfiguration(), errorHandler); } catch (ParserGeneratorInvocationException e) { e.printStackTrace(); fail("Failed to generate grammar:" + e.getMessage()); } assertFalse("Must have completed without (critical) errors", errorHandler.hasFailedWithError()); } public void generateParser() { ByteArrayOutputStream errByteStream = new ByteArrayOutputStream(); PrintStream systemErrOld = redirectSystemErrTo(errByteStream); SystemOutErrorHandler errorHandler = new SystemOutErrorHandler(); try { TCSParserGenerator generator = TCSParserGeneratorFactory.INSTANCE.createTCSParserGenerator(); generator.generateParserFromGrammar(testConfig.getTargetConfiguration(), errorHandler); } catch (ParserGeneratorInvocationException e) { e.printStackTrace(); fail("Failed go generate Parser/Lexer class: " + e.getMessage()); } finally { restoreOldSystemErr(systemErrOld); } checkSystemErrForANTLRErrosAndFailIfNecessary(errByteStream); assertFalse("Must have completed without (critical) errors. See syserr.", errorHandler.hasFailedWithError()); } public void compileParser() { ByteArrayOutputStream errByteStream = new ByteArrayOutputStream(); PrintStream systemErrOld = redirectSystemErrTo(errByteStream); try { int success = Main.compile(new String[] { testConfig.getRelativePathToGeneratedLexerClass(), testConfig.getRelativePathToGeneratedParserClass(), "-cp", getSourceRoot(Lexer.class) + File.pathSeparator + getSourceRoot(ObservableInjectingParser.class) + File.pathSeparator + getSourceRoot(IModelElementProxy.class) + File.pathSeparator + getSourceRoot(SyntaxRegistryFacade.class) + File.pathSeparator + getSourceRoot(ResourceSet.class) + File.pathSeparator + getSourceRoot(Notifier.class) + File.pathSeparator + getSourceRoot(RuleNameFinder.class)}); if (success != 0) { fail("Parser compilation failed with code '" + success + "'. Messages: \n" + errByteStream.toString()); } } finally { restoreOldSystemErr(systemErrOld); } } public ParserFacade loadParserFacade(ClassLookup classLookup) throws ParserGeneratorInvocationException, InvalidParserImplementationException { // try loading compiled classes try { @SuppressWarnings("unchecked") Class<? extends Lexer> lexerclass = (Class<? extends Lexer>) classLookup.loadClass(testConfig .getClassNameOfCompiledLexer()); @SuppressWarnings("unchecked") Class<? extends ObservableInjectingParser> parserclass = (Class<? extends ObservableInjectingParser>) classLookup .loadClass(testConfig.getClassNameOfCompiledParser()); ParserFacade facade = new ParserFacade(parserclass, lexerclass, testConfig.getLanguageName()); return facade; } catch (ClassNotFoundException cnfe) { throw new ParserGeneratorInvocationException("Can't find generated classes " + testConfig.getClassNameOfCompiledLexer() + " and " + testConfig.getClassNameOfCompiledLexer() + ". Try to do an Eclipse refresh on the project.", cnfe); } } protected static PrintStream redirectSystemErrTo(ByteArrayOutputStream errByteStream) { PrintStream originalSystemErr = System.err; System.setErr(new PrintStream(errByteStream)); return originalSystemErr; } protected static void restoreOldSystemErr(PrintStream systemErr) { System.setErr(systemErr); } /** * If antlr wrote to System.err, fail the test with ANTLR messages. If err is empty, continue */ protected static void checkSystemErrForANTLRErrosAndFailIfNecessary(ByteArrayOutputStream errByteStream) { String errString = errByteStream.toString().trim(); if (!"".equals(errString)) { if (errString.toLowerCase().indexOf("error") > -1) { // ignore // warnings written to System.err fail(errString); } else { System.out.println(errString); errByteStream.reset(); // discarding warnings from stream, so // that error only shows errors } } } public void cleanUp() { File genDir = new File(testConfig.getRelativePathToGeneratedFiles()); assertTrue(genDir.getAbsolutePath() + " is supposed to be a directory", genDir.isDirectory()); for (File file : genDir.listFiles()) { if ( file.getName().startsWith(testConfig.getLanguageName()) && file.getName().endsWith(".java") || file.getName().startsWith(testConfig.getLanguageName()) && file.getName().endsWith(".class") || file.getName().startsWith(testConfig.getLanguageName()) && file.getName().endsWith(".g") || file.getName().equals(testConfig.getLanguageName() + ".tokens")) { file.delete(); } } } }