blob: da0d230d94cd5c0e3c2f4ddd621fa0b71afbd333 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2007 Borland Software Corporation
*
* 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:
* Borland Software Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.m2m.tests.qvt.oml;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Set;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.FileLocator;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Platform;
import org.eclipse.emf.codegen.ecore.Generator;
import org.eclipse.jdt.core.IClasspathContainer;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaCore;
import org.osgi.framework.Bundle;
/**
* This strategy is copied from GMF project unittest (www.eclipse.org/gmf)
*
* ALMOST TRUE: With PDE, we need source code in the running workspace to allow compilation of our code
* (because PDE doesn't reexport set of plugins from it's running configuration, and it's no longer possible
* to set Target Platform to "same as running" as it was back in Eclipse 2.x).
*
* !!! NEW !!!
*
* Now, we managed to compile against linked binary folders, although using linked content instead of plugins
* requires us to explicitly add some plugins earlier available through plugin re-export (namely, oe.jface.text)
*
*
* Classloading works because there's -dev argument in the command line. With PDE launch, it's done by PDE.
* Without PDE, running tests as part of the build relies on Eclipse Testing Framework's org.eclipse.test_3.1.0/library.xml
* which specifies "-dev bin,runtime". Once it's not specified, or new format (properties file with plugin-id=binfolder)
* is in use, classloading of the generated code will fail and another mechanism should be invented then.
*
* If you get ClassNotFoundException while running tests in PDE environment, try to set read-only attribute for the next file:
* 'development-workspace'\.metadata\.plugins\org.eclipse.pde.core\'JUnitLaunchConfigName'\dev.properties
* @author artem
*/
public class RuntimeWorkspaceSetup {
private static RuntimeWorkspaceSetup INSTANCE;
/**
* Copy of <code>PDECore.CLASSPATH_CONTAINER_ID</code>
*/
private static final String PLUGIN_CONTAINER_ID = "org.eclipse.pde.core.requiredPlugins"; //$NON-NLS-1$
private boolean isDevLaunchMode;
private RuntimeWorkspaceSetup() {
isDevLaunchMode = isDevLaunchMode();
}
public static RuntimeWorkspaceSetup getInstance() {
if(INSTANCE == null) {
try {
INSTANCE = new RuntimeWorkspaceSetup();
INSTANCE.initFull();
} catch (Exception e) {
throw new RuntimeException("Runtime Unittest workspace error", e); //$NON-NLS-1$
}
}
return INSTANCE;
}
public boolean getIsDevLaunchMode() {
return isDevLaunchMode;
}
/**
* Copy (almost, except for strange unused assignment) of <code>PDECore.isDevLaunchMode()</code>
*/
private static boolean isDevLaunchMode() {
String[] args = Platform.getApplicationArgs();
for (int i = 0; i < args.length; i++) {
if (args[i].equals("-pdelaunch")) { //$NON-NLS-1$
return true;
}
}
return false;
}
private RuntimeWorkspaceSetup initFull() throws Exception {
init(new String[] {
"org.eclipse.emf.ecore", //$NON-NLS-1$
"org.eclipse.emf.common", //$NON-NLS-1$
"org.eclipse.m2m.qvt.oml", //$NON-NLS-1$
"org.eclipse.m2m.qvt.oml.samples", //$NON-NLS-1$
"org.eclipse.m2m.qvt.oml.ocl.emf.libraries", //$NON-NLS-1$
"org.eclipse.m2m.tests.qvt.oml", //$NON-NLS-1$*/
});
return this;
}
private void init(String... pluginsToImport) throws Exception {
ensureJava14();
if (isDevLaunchMode) {
// Need to get some gmf source code into target workspace
importDevPluginsIntoRunTimeWorkspace(pluginsToImport);
}
}
public static IProject getSOSProject() {
return ResourcesPlugin.getWorkspace().getRoot().getProject(".SOSProject"); //$NON-NLS-1$
}
/**
* Another approach - output binary folders of required plugins are linked as subfolders
* of our own sosProject (created in the target workspace). Then, we could use library classpathEntries
* (details why we should use _workspace_ paths for libraries could be found at
* <code>org.eclipse.jdt.internal.core.builder.NameEnvironment#computeClasspathLocations</code>)
*
* TODO don't assume workspace is clear, check sosProject existence first
* TODO utilize GenDiagram.requiredPluginIDs once it's a field (i.e. add oe.jface.text and don't create plugin project then, just plain project with links
*/
private void importDevPluginsIntoRunTimeWorkspace(String[] pluginIDs) throws CoreException {
IProject p = getSOSProject();
final Path srcPath = new Path('/' + p.getName() + "/src"); //$NON-NLS-1$
Generator.createEMFProject(srcPath, null, Collections.EMPTY_LIST, new NullProgressMonitor(), Generator.EMF_PLUGIN_PROJECT_STYLE, null);
StringBuffer pluginXmlContent = new StringBuffer();
pluginXmlContent.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<?eclipse version=\"3.0\"?>\n<plugin "); //$NON-NLS-1$
pluginXmlContent.append(" version=\"1.0.0\" name='%providerName' id='"); //$NON-NLS-1$
pluginXmlContent.append(p.getName());
pluginXmlContent.append("'>\n<requires>\n"); //$NON-NLS-1$
pluginXmlContent.append("<import plugin='org.eclipse.jface.text' export='true'/>\n"); //$NON-NLS-1$
pluginXmlContent.append("<import plugin='org.eclipse.ui.views.properties.tabbed' export='true'/>\n"); //$NON-NLS-1$
ClasspathEntry[] classpathEntries = getClasspathEntries(pluginIDs);
for (int i = 0; i < classpathEntries.length; i++) {
classpathEntries[i].importTo(p, pluginXmlContent);
}
pluginXmlContent.append("</requires>\n</plugin>"); //$NON-NLS-1$
p.getFile("plugin.xml").create(new ByteArrayInputStream(pluginXmlContent.toString().getBytes()), true, new NullProgressMonitor()); //$NON-NLS-1$
}
private ClasspathEntry[] getClasspathEntries(String[] pluginIDs) {
ArrayList<ClasspathEntry> entries = new ArrayList<ClasspathEntry>(pluginIDs.length);
for (int i = 0; i < pluginIDs.length; i++) {
ClasspathEntry nextEntry = new ClasspathEntry(pluginIDs[i]);
if (nextEntry.isValid()) {
entries.add(nextEntry);
} else {
System.out.println("Bundle " + pluginIDs[i] + " is missing, skipped."); //$NON-NLS-1$ //$NON-NLS-2$
}
}
return entries.toArray(new ClasspathEntry[entries.size()]);
}
private IJavaProject asJavaProject(IProject p) {
return JavaCore.create(p);
}
/**
* TODO uniqueClassPathEntries is not needed if diagramProj gets here only once. It's not the case
* now - refactor LinkCreationConstraintsTest to utilize genProject created in AuditRulesTest (?)
* TODO refactor with ClasspathContainerInitializer - just for the sake of fixing the knowledge
*/
public void updateClassPath(IProject aProject) throws CoreException {
if (!isDevLaunchMode) {
return;
}
IResource[] members;
try {
members = getSOSProject().members();
} catch (CoreException ex) {
ex.printStackTrace();
members = new IResource[0];
}
final IJavaProject sosJavaPrj = asJavaProject(getSOSProject());
if(!JavaCore.create(aProject).exists()) {
return;
}
IClasspathEntry[] cpOrig = asJavaProject(aProject).getRawClasspath();
ArrayList<IClasspathEntry> rv = new ArrayList<IClasspathEntry>(10 + cpOrig.length + members.length);
IClasspathContainer c = JavaCore.getClasspathContainer(new Path(PLUGIN_CONTAINER_ID), sosJavaPrj);
if (c != null) {
IClasspathEntry[] cpAdd = c.getClasspathEntries();
rv.addAll(Arrays.asList(cpAdd));
}
for (int i = 0; i < members.length; i++) {
if (!members[i].isLinked()) {
continue;
}
rv.add(JavaCore.newLibraryEntry(members[i].getFullPath(), null, null));
}
final Set<IPath> uniqueClassPathEntries = new HashSet<IPath>();
IClasspathEntry[] cpOrigResolved = asJavaProject(aProject).getResolvedClasspath(true);
for (int i = 0; i < cpOrigResolved.length; i++) {
uniqueClassPathEntries.add(cpOrigResolved[i].getPath());
}
for (Iterator it = rv.iterator(); it.hasNext();) {
IClasspathEntry next = (IClasspathEntry) it.next();
if (uniqueClassPathEntries.contains(next.getPath())) {
it.remove();
} else {
uniqueClassPathEntries.add(next.getPath());
}
}
rv.addAll(Arrays.asList(cpOrig));
IClasspathEntry[] cpNew = rv.toArray(new IClasspathEntry[rv.size()]);
asJavaProject(aProject).setRawClasspath(cpNew, new NullProgressMonitor());
}
/**
* at least
*/
@SuppressWarnings("unchecked")
private void ensureJava14() {
if (!JavaCore.VERSION_1_4.equals(JavaCore.getOption(JavaCore.COMPILER_SOURCE))) {
Hashtable<String,String> options = JavaCore.getOptions();
options.put(JavaCore.COMPILER_COMPLIANCE, JavaCore.VERSION_1_4);
options.put(JavaCore.COMPILER_SOURCE, JavaCore.VERSION_1_4);
options.put(JavaCore.COMPILER_CODEGEN_TARGET_PLATFORM, JavaCore.VERSION_1_4);
JavaCore.setOptions(options);
}
}
private class ClasspathEntry {
private String myPluginID;
private URL myBundleURL;
private File myBundleFile;
private File myClassesContainerFile;
private ClasspathEntry(String pluginID) {
myPluginID = pluginID;
}
public void importTo(IProject p, StringBuffer pluginXmlContent) {
if (!getClassesContainerFile().exists()) {
pluginXmlContent.append("<import plugin='"); //$NON-NLS-1$
pluginXmlContent.append(myPluginID);
pluginXmlContent.append("' export='true'/>\n"); //$NON-NLS-1$
} else {
if (getClassesContainerFile().isDirectory()) {
String entryName = getBundleFile().getName().replace('.', '_');
IFolder folder = p.getFolder(entryName);
try {
folder.createLink(new Path(getClassesContainerFile().getAbsolutePath()), IResource.REPLACE, new NullProgressMonitor());
} catch (CoreException e) {
e.printStackTrace();
}
} else if (getClassesContainerFile().isFile()) {
String entryName = getClassesContainerFile().getName();
IFile file = p.getFile(entryName);
try {
file.createLink(new Path(getClassesContainerFile().getAbsolutePath()), IResource.REPLACE, new NullProgressMonitor());
} catch (CoreException e) {
e.printStackTrace();
}
}
}
}
private File getClassesContainerFile() {
if (myClassesContainerFile == null) {
myClassesContainerFile = new File(getBundleFile(), getRelativePath());
}
return myClassesContainerFile;
}
private File getBundleFile() {
if (myBundleFile == null) {
myBundleFile = new File(getBundleURL().getFile());
}
return myBundleFile;
}
private String getRelativePath() {
return "/bin/"; //$NON-NLS-1$
}
private URL getBundleURL() {
if (myBundleURL == null) {
Bundle bundle = Platform.getBundle(myPluginID);
if (bundle == null) {
//Do not throw exception. This allows requiring lite runtime plugin and not failing in configurations where it is not present.
return null;
}
try {
myBundleURL = FileLocator.resolve(bundle.getEntry("/")); //$NON-NLS-1$
} catch (IOException e) {
e.printStackTrace();
}
}
return myBundleURL;
}
public boolean isValid() {
return getBundleURL() != null;
}
}
}