/*******************************************************************************
* 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.incremental.testbase;
import static com.sap.furcas.test.util.CompilationHelper.getSourceRoot;
import static org.junit.Assert.fail;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintStream;
import java.util.Collection;
import org.antlr.runtime.Lexer;
import org.eclipse.emf.common.notify.Notifier;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.resource.ResourceSet;
import com.sap.emf.bundlelistener.EcorePackageLoadListener;
import com.sap.furcas.ide.parserfactory.AbstractParserFactory;
import com.sap.furcas.parsergenerator.TCSSyntaxContainerBean;
import com.sap.furcas.runtime.common.exceptions.ParserGeneratorInvocationException;
import com.sap.furcas.runtime.common.exceptions.ParserInstantiationException;
import com.sap.furcas.runtime.common.interfaces.IModelElementProxy;
import com.sap.furcas.runtime.parser.PartitionAssignmentHandler;
import com.sap.furcas.runtime.parser.antlr3.ITokenFactory;
import com.sap.furcas.runtime.parser.impl.ObservableInjectingParser;
import com.sap.furcas.runtime.parser.testbase.ClassLookup;
import com.sap.furcas.runtime.parser.testbase.GeneratedParserBasedTest;
import com.sap.furcas.runtime.parser.testbase.GeneratedParserTestConfiguration;
import com.sap.furcas.runtime.parser.testbase.ParserGenerator;
import com.sap.furcas.runtime.referenceresolving.SyntaxRegistryFacade;
import com.sap.furcas.runtime.tcs.RuleNameFinder;
import com.sap.ide.cts.parser.incremental.IncrementalParserFacade;
import com.sap.ide.cts.parser.incremental.antlr.ANTLRParserFactory;
import com.sun.tools.javac.Main;
/**
* A class that allows to generate a language specific parser and parserfactory from a given TCS file.
* Its methods must be called in the follwoing order:
*
* generateGrammar(),
* generateParser(),
* generateParserFactory()
* compileParser(),
* compileParserFactory(),
* 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 ParserAndFactoryGenerator extends ParserGenerator {
private final GeneratedParserAndFactoryTestConfiguration testConfig;
public ParserAndFactoryGenerator(GeneratedParserAndFactoryTestConfiguration testConfig) {
super(testConfig);
this.testConfig = testConfig;
}
public void generateParserFactory(TCSSyntaxContainerBean syntaxBean) {
File parserFactoryFile = new File(testConfig.getRelativePathToGeneratedParserFactoryClass());
PrintStream out = null;
try {
out = new PrintStream(new FileOutputStream(parserFactoryFile));
out.println("package " + testConfig.getTargetConfiguration().getParserTargetPackageName() + ";");
out.println("import " + testConfig.getClassNameOfCompiledLexer() + ";");
out.println("import " + testConfig.getClassNameOfCompiledParser() + ";");
out.println("import com.sap.furcas.ide.parserfactory.AbstractParserFactory;");
out.println("import org.eclipse.emf.ecore.resource.ResourceSet;");
out.println("import org.eclipse.emf.ecore.EPackage;");
out.println("import org.eclipse.emf.common.util.URI;");
out.println("import java.util.Set;");
out.println("import java.util.HashSet;");
out.println("import java.util.Collections;");
out.println("public class " + testConfig.getParserFactoryName() + " extends AbstractParserFactory<"
+ testConfig.getParserName() + ", " + testConfig.getLexerName() + "> { ");
out.println(" private static final String CLASS_LANGUAGE_ID = \"" + testConfig.getLanguageName() + "\"; ");
out.println(" @Override ");
out.println(" public Class<" + testConfig.getLexerName() + "> getLexerClass() { ");
out.println(" return " + testConfig.getLexerName() + ".class;");
out.println(" } ");
out.println(" @Override ");
out.println(" public Class<" + testConfig.getParserName() + "> getParserClass() { ");
out.println(" return " + testConfig.getParserName() + ".class;");
out.println(" } ");
out.println(" @Override ");
out.println(" public String getLanguageId() { ");
out.println(" return CLASS_LANGUAGE_ID;");
out.println(" }");
out.println(" @Override");
out.println(" public Set<URI> getMetamodelURIs() {");
out.println( getUriListAsString(testConfig.getSourceConfiguration().getReferenceScope()));
out.println(" }");
out.println(" @Override");
out.println(" public URI getSyntaxResourceURI() {");
out.println(" return URI.createURI(\"" + syntaxBean.getSyntax().eResource().getURI() + "\");");
out.println(" }");
out.println(" @Override");
out.println(" public Set<URI> getAdditionalQueryScope() {");
out.println( getUriListAsString(testConfig.getAdditionalQueryScope()));
out.println(" }");
out.println("}");
out.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
fail("Failed to generate parser factory: " + e.getMessage());
} finally {
if (out != null) {
out.close();
}
}
}
private String getUriListAsString(Collection<URI> uris) {
StringBuilder builder = new StringBuilder();
builder.append("HashSet<URI> uris = new HashSet<URI>();\n");
for (URI uri : uris) {
builder.append("uris.add(URI.createURI(\"" + uri.toString() + "\"));\n");
}
builder.append("return uris;\n");
return builder.toString();
}
public void compileParserFactory() {
ByteArrayOutputStream errByteStream = new ByteArrayOutputStream();
PrintStream systemErrOld = redirectSystemErrTo(errByteStream);
try {
int success = Main.compile(new String[] {
testConfig.getRelativePathToGeneratedParserFactoryClass(),
testConfig.getRelativePathToGeneratedParserClass(),
testConfig.getRelativePathToGeneratedLexerClass(),
"-cp",
getSourceRoot(AbstractParserFactory.class)
+ File.pathSeparator + getSourceRoot(ResourceSet.class)
+ File.pathSeparator + getSourceRoot(Notifier.class)
+ File.pathSeparator + getSourceRoot(ANTLRParserFactory.class)
+ File.pathSeparator + getSourceRoot(ITokenFactory.class)
+ File.pathSeparator + getSourceRoot(RuleNameFinder.class)
+ File.pathSeparator + getSourceRoot(IModelElementProxy.class)
+ File.pathSeparator + getSourceRoot(Lexer.class)
+ File.pathSeparator + getSourceRoot(SyntaxRegistryFacade.class)
+ File.pathSeparator + getSourceRoot(EcorePackageLoadListener.class)});
if (success != 0) {
fail("Parser compilation failed with code '" + success + "'. Messages: \n" + errByteStream.toString());
}
} finally {
restoreOldSystemErr(systemErrOld);
}
}
public IncrementalParserFacade loadIncrementalParserFacade(ClassLookup classLookup, ResourceSet resourceSet,
PartitionAssignmentHandler partitionAssignmentHandler) throws ParserGeneratorInvocationException, InstantiationException,
IllegalAccessException, ParserInstantiationException {
AbstractParserFactory<? extends ObservableInjectingParser, ? extends Lexer> parserFactory =
loadParserFactory(classLookup).newInstance();
return new IncrementalParserFacade(parserFactory, resourceSet, partitionAssignmentHandler);
}
@SuppressWarnings("unchecked")
private Class<AbstractParserFactory<? extends ObservableInjectingParser, ? extends Lexer>> loadParserFactory(
ClassLookup classLookup) throws ParserGeneratorInvocationException {
try {
// try loading compiled classes
Class<?> parserFactoryClass = classLookup.loadClass(testConfig.getClassNameOfCompiledParserFactory());
return (Class<AbstractParserFactory<? extends ObservableInjectingParser, ? extends Lexer>>) parserFactoryClass;
} catch (ClassNotFoundException cnfe) {
throw new ParserGeneratorInvocationException("Can't find generated class "
+ testConfig.getClassNameOfCompiledParserFactory() + ". Try to do an Eclipse refresh on the project.", cnfe);
}
}
}