package com.sap.furcas.ide.projectwizard.test;
import static org.junit.Assert.fail;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Platform;
import org.junit.Test;
import org.osgi.framework.Bundle;
import com.sap.furcas.ide.projectwizard.util.CodeGenerationException;
import com.sap.furcas.ide.projectwizard.util.ProjectInfo;
import com.sap.furcas.ide.projectwizard.util.SourceCodeFactory;
import com.sap.furcas.ide.projectwizard.wizards.FurcasWizard;
import com.sun.tools.javac.Main;
/**
* This class includes the test method <code>GeneratedClassesTest</code> and provides methods to generate the classes
* (ParserFactory, Activator, Editor, ...) as generated by the projectwizard and tries to build them making sure that
* their dependencies are still up to date and working. It fails if one of the classes can't compile.
*
* @author Frederik Petersen (D054528)
* @author Axel Uhl (D043530)
*
*/
public class GeneratedClassesTest {
private static final String BIN_DIR_NAME = "bin";
private final ArrayList<String> nonWorkspacePlugins = new ArrayList<String>();
private final ArrayList<String> workspacePlugins = new ArrayList<String>();
/**
* Maps plain bundle names (e.g., "org.eclipse.emf") to a {@link File} location in the workspace in case
* a (sub)directory by the single name given by the key exists in the workspace that contains a "bin" folder.
* If <code>null</code>, the workspace hasn't been analyzed yet.
*/
private Map<String, File> workspacePluginCandidates;
/**
* This test method calls the other methods in this class to generate, compile and clean the java classes.
*/
@Test
public void compileGeneratedClasses() throws Exception {
ProjectInfo pi = new ProjectInfo();
configureProjectInfo(pi);
FurcasWizard wizard = new FurcasWizard();
wizard.structuredProcess(pi, new NullProgressMonitor());
if (wizard.isHadError()) {
fail("Wizard has ERRORs. See console output for details.");
}
IWorkspace workspace = ResourcesPlugin.getWorkspace();
IProject project = workspace.getRoot().getProject(pi.getProjectName());
try {
compileClasses(project);
} finally {
project.delete(true, new NullProgressMonitor());
}
}
private void compileClasses(IProject project) throws IOException {
ByteArrayOutputStream errByteStream = new ByteArrayOutputStream();
PrintStream systemErrOld = redirectSystemErrTo(errByteStream);
String requiredBundles = getRequiredBundles();
List<File> filesToCompile = findJavaSources(Platform.getLocation().append(project.getFullPath()).toOSString());
try {
int success = Main.compile(composeCompilationOptions(filesToCompile, requiredBundles));
if (success != 0) {
fail("Parser compilation failed with code '" + success + "'. Messages: \n" + errByteStream.toString()+"\nClasspath was: "+requiredBundles);
}
} finally {
restoreOldSystemErr(systemErrOld);
cleanGenerationFolder(filesToCompile);
}
}
private List<File> findJavaSources(String folderWithFilesToCompile) {
File dir = new File(folderWithFilesToCompile);
List<File> files = new ArrayList<File>();
return findJavaSourcesRecursively(dir, files);
}
private List<File> findJavaSourcesRecursively(File dir, List<File> files) {
for (File file : dir.listFiles()) {
if (file.getAbsolutePath().endsWith(".java")) {
files.add(file);
} else if (file.isDirectory()) {
findJavaSourcesRecursively(file, files);
}
}
return files;
}
private String[] composeCompilationOptions(List<File> filesToCompile, String requiredBundles) {
String[] compilationOptions = new String[filesToCompile.size() + 2];
for (int i=0; i<filesToCompile.size(); i++) {
compilationOptions[i] = filesToCompile.get(i).getPath();
}
compilationOptions[compilationOptions.length-2] = "-cp";
compilationOptions[compilationOptions.length-1] = requiredBundles;
return compilationOptions;
}
/**
* This plugin uses variables <code>nonWorkspacePlugins</code> and <code>workspacePlugins</code> to build a String containing
* the classpath for compiling the java classes generated by the wizard.
*
* @return The classpath for the compilation process.
*/
private String getRequiredBundles() throws IOException, IllegalArgumentException, SecurityException {
getManifestPluginEntries();
String eclipsePath;
if (System.getProperty("eclipse.location")!=null) {
eclipsePath = System.getProperty("eclipse.location");
} else {
eclipsePath = System.getProperty("target.location");
}
StringBuffer requiredBundles = new StringBuffer("./lib/org.eclipse.swt.gtk.linux.x86_64_3.6.1.v3655c.jar"
+ File.pathSeparator + "./lib/static");
for (String plugin : workspacePlugins) {
requiredBundles.append(File.pathSeparator + getWorkspacePluginCandidates().get(plugin) +
File.separator + BIN_DIR_NAME);
}
// The non-workspace bundles are tricky to find, particularly when executing in a Maven
// environment. There, the bundles of the executing JUnit plugin test shell are obtained
// from the Maven repository under .m2/repository/... and may have slightly different versions
// as compared to the Eclipse instance installed under ${eclipse.location}. Therefore, the
// lookup should first check ${target.location} before ${eclipse.location}.
// Furthermore, the bundle JAR files may be located in some nested directory structure, such
// as .m2/repository/p2/osgi/bundle/org.eclipse.ui/3.6.2.M20110203-1100 for the bundle
// org.eclipse.ui-3.6.2.M20110203-1100.jar. The bundle location can be determined by
// Bundle.getLocation() and gives a hint at the physical location of the JAR file. Trailing
// slashes have to be removed.
Object[] bundles = nonWorkspacePlugins.toArray();
for (int i = 0; i < bundles.length; i++) {
String bundlePath = null;
Bundle bundle = Platform.getBundle((String) bundles[i]);
if (bundle == null) {
System.err.println("Unable to find bundle "+bundles[i]);
} else {
try {
System.out.println("Trying to find bundle "+bundle);
Object bundleData = bundle.getClass().getMethod("getBundleData").invoke(bundle);
System.out.println(" got bundle data "+bundleData);
Object bundleFile = bundleData.getClass().getMethod("getBundleFile").invoke(bundleData);
System.out.println(" got bundle file "+bundleFile);
File baseFile = (File) bundleFile.getClass().getMethod("getBaseFile").invoke(bundleFile);
System.out.println(" got base file "+baseFile);
bundlePath = baseFile.getCanonicalPath();
} catch (Exception e) {
e.printStackTrace();
// it didn't work; try something else
}
if (bundlePath == null) {
File bundleJar = findBundleJar(bundle);
if (bundleJar != null) {
bundlePath = bundleJar.getCanonicalPath();
} else {
String bundleJarName = bundle.toString().split(" ")[0] + ".jar";
if (eclipsePath.contains("/")) {
if (eclipsePath.endsWith("/")) {
bundlePath = eclipsePath + "plugins/" + bundleJarName;
} else {
bundlePath = eclipsePath + "/plugins/" + bundleJarName;
}
} else {
if (eclipsePath.endsWith("\\")) {
bundlePath = eclipsePath + "plugins\\" + bundleJarName;
} else {
bundlePath = eclipsePath + "\\plugins\\" + bundleJarName;
}
}
}
}
File f = new File(bundlePath);
try {
System.out.print("Canonical bundle path: "+f.getCanonicalPath());
System.out.println(f.exists() ? " EXISTS" : " DOES NOT EXIST");
} catch (IOException e) {
e.printStackTrace();
}
requiredBundles.append(File.pathSeparator + bundlePath);
}
}
return requiredBundles.toString();
}
private File findBundleJar(Bundle bundle) {
File searchStartDir = new File(System.getProperty("target.location"));
return findRecursively(searchStartDir, bundle.toString());
}
private File findRecursively(File d, String string) {
if (d.exists() && d.isDirectory()) {
if (Arrays.asList(d.list()).contains(string)) {
return new File(d, string);
} else {
for (File entry : d.listFiles()) {
File result = findRecursively(entry, string);
if (result != null) {
return result;
}
}
}
}
return null;
}
/**
* Gets rid of all generated .java and .class files
*/
private void cleanGenerationFolder(List<File> filesToCompile) {
for (File source : filesToCompile) {
source.delete();
File compiled = new File(source.getPath().replace(".java", ".class"));
if (compiled.exists()) {
compiled.delete();
}
}
}
/**
* Gives default values to the ProjectInfo instance.
*
* @param pi
*/
public static void configureProjectInfo(ProjectInfo pi) {
pi.setLoadMetamodel(false);
pi.setClassName("ExampleClass");
pi.setFileExtension("dsl");
pi.setLanguageName("Mydsl");
pi.setModelPath("/my.dsl.metamodel/model/Mydsl.ecore");
pi.setNsURI("http://www.example.com/mydsl");
pi.setProjectName("my.dsl");
pi.setAutoResolve(false);
}
private static PrintStream redirectSystemErrTo(ByteArrayOutputStream errByteStream) {
PrintStream originalSystemErr = System.err;
System.setErr(new PrintStream(errByteStream));
return originalSystemErr;
}
private static void restoreOldSystemErr(PrintStream systemErr) {
System.setErr(systemErr);
}
/**
* Generates the contents of the manifest as generated by the wizard and reads the entries
* into the variables: <code>nonWorkspacePlugins</code> and <code>workspacePlugins</code>.
*/
private void getManifestPluginEntries() {
String[] lines = null;
SourceCodeFactory codeFactory = new SourceCodeFactory();
ProjectInfo pi = new ProjectInfo();
configureProjectInfo(pi);
try {
String contents = codeFactory.createManifest(pi,"");
lines = contents.split("\n");
} catch (CodeGenerationException e) {
e.printStackTrace();
}
for (String i : lines) {
if (i.contains(";")) {
i = i.split(";")[0];
}
if (i.startsWith("Require") || i.startsWith(" ")) {
String modifier = i.replaceAll("Require-Bundle: ", "").trim();
modifier = modifier.replace(",", "");
if (isBundleInWorkspace(modifier)) {
workspacePlugins.add(modifier);
} else {
nonWorkspacePlugins.add(modifier);
}
}
}
}
private boolean isBundleInWorkspace(String bundleName) {
File bundleDir = getWorkspacePluginCandidates().get(bundleName);
return bundleDir != null;
}
private Map<String, File> getWorkspacePluginCandidates() {
if (workspacePluginCandidates == null) {
workspacePluginCandidates = determineWorkspacePluginCandidates();
}
return workspacePluginCandidates;
}
private Map<String, File> determineWorkspacePluginCandidates() {
Map<String, File> result = new HashMap<String, File>();
File workspaceRoot = new File("../.."); // assuming we're running in DSLEngineering/*wizard.test
assert(workspaceRoot.exists() && workspaceRoot.isDirectory());
findRecursively(workspaceRoot, result);
return result;
}
private boolean containsBin(File bundleDir) {
assert(bundleDir.exists() && bundleDir.isDirectory());
for (File f : bundleDir.listFiles()) {
if (f.getName().equals(BIN_DIR_NAME)) {
return true;
}
}
return false;
}
private void findRecursively(File d, Map<String, File> result) {
if (d.exists() && d.isDirectory()) {
if (containsBin(d)) {
result.put(d.getName(), d);
} else {
for (File entry : d.listFiles()) {
findRecursively(entry, result);
}
}
}
}
}