package org.eclipse.xtend.backend.ui.compiler;
import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
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.IResourceDelta;
import org.eclipse.core.resources.IResourceDeltaVisitor;
import org.eclipse.core.resources.IResourceVisitor;
import org.eclipse.core.resources.IncrementalProjectBuilder;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.ProgressMonitorWrapper;
import org.eclipse.core.runtime.SubMonitor;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IPackageFragmentRoot;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.xtend.backend.common.BackendTypesystem;
import org.eclipse.xtend.backend.compiler.BackendCompilerFacade;
import org.eclipse.xtend.backend.compiler.templates.java5.Java5CompilerFacade;
import org.eclipse.xtend.backend.types.CompositeTypesystemFactory;
import org.eclipse.xtend.backend.ui.Activator;
import org.eclipse.xtend.backend.ui.middleend.LanguageSpecificMiddleEndConfigurer;
import org.eclipse.xtend.middleend.MiddleEnd;
import org.eclipse.xtend.middleend.MiddleEndFactory;
import org.eclipse.xtend.middleend.plugins.LanguageSpecificMiddleEnd;
public class BackendBuilder extends IncrementalProjectBuilder {
public static final String BACKEND_GEN_FOLDER = "backend-gen";
public static final String BUILDER_ID = "org.eclipse.xtend.backend.compiler.BackendBuilder";
private final static Log _log = LogFactory.getLog (BackendBuilder.class);
private List<LanguageSpecificMiddleEnd> middleends;
class BackendDeltaVisitor implements IResourceDeltaVisitor, IResourceVisitor {
private final IProgressMonitor _monitor;
private List<LanguageSpecificMiddleEnd> _middleends;
public BackendDeltaVisitor(final IProgressMonitor monitor) {
this._monitor = monitor;
}
/*
* (non-Javadoc)
*
* @see
* org.eclipse.core.resources.IResourceDeltaVisitor#visit(org.eclipse
* .core.resources.IResourceDelta)
*/
public boolean visit(final IResourceDelta delta) throws CoreException {
final IResource resource = delta.getResource();
if (_middleends == null)
_middleends = createMiddleEnd ().getLanguageSpecificMiddleEnds();
if (isBackendCompilableResource (resource, _middleends)) {
switch (delta.getKind()) {
case IResourceDelta.ADDED:
// handle added resource
onAddOrUpdate ((IFile) resource, _monitor);
break;
case IResourceDelta.REMOVED:
// handle removed resource
onRemoval ((IFile) resource, _monitor);
break;
case IResourceDelta.CHANGED:
// handle changed resource
onAddOrUpdate ((IFile) resource, _monitor);
break;
}
}
_monitor.worked(1);
return true;
}
public boolean visit(final IResource resource) {
if (_middleends == null)
_middleends = createMiddleEnd ().getLanguageSpecificMiddleEnds();
if (isBackendCompilableResource (resource, _middleends)) {
onAddOrUpdate ((IFile) resource, _monitor);
}
_monitor.worked(1);
return true;
}
}
@Override
protected IProject[] build(int kind, Map args, IProgressMonitor monitor)
throws CoreException {
ClassLoader origCl = Thread.currentThread ().getContextClassLoader();
try {
final IJavaProject jp = JavaCore.create (getProject());
URLClassLoader backendCl = new URLClassLoader (getBuildClasspath (jp), origCl);
Thread.currentThread ().setContextClassLoader (backendCl);
if (monitor != null) {
final String taskName = "Compiling project " + getProject ().getName();
monitor = new ProgressMonitorWrapper (monitor) {
@Override
public void subTask (String name) {
super.subTask (taskName + name);
}
};
}
SubMonitor progress = SubMonitor.convert (monitor, 1);
if (kind == FULL_BUILD) {
doFullBuild (progress.newChild (1));
} else {
final IResourceDelta delta = getDelta (getProject());
if (delta == null) {
doFullBuild (progress.newChild (1));
}
else {
doIncrementalBuild (delta, progress.newChild (1));
}
}
} finally {
Thread.currentThread ().setContextClassLoader (origCl);
if (monitor != null)
monitor.done ();
}
return null;
}
protected void doFullBuild (SubMonitor progress) {
try {
final IJavaProject jp = JavaCore.create(getProject());
final IPackageFragmentRoot[] roots = jp.getPackageFragmentRoots();
Map<IResource, IPath> resources = new HashMap<IResource, IPath> ();
String defaultCharset = getProject().getDefaultCharset();
List<String> srcFolders = new ArrayList<String>(roots.length);
for (IPackageFragmentRoot iPackageFragmentRoot : roots) {
if (! iPackageFragmentRoot.getPath().equals (getOutputFolder(progress))) {
if (iPackageFragmentRoot.getResource() != null && ! iPackageFragmentRoot.isArchive()) {
srcFolders.add (iPackageFragmentRoot.getResource().getRawLocation().toOSString());
}
getAllResources (iPackageFragmentRoot, iPackageFragmentRoot.getResource(), resources);
// defaultCharset = iPackageFragmentRoot.getResource().getProject().getDefaultCharset();
}
}
Set<String> resNames = new HashSet<String> (resources.size());
List<LanguageSpecificMiddleEnd> middleends = createMiddleEnd ().getLanguageSpecificMiddleEnds();
for (Entry<IResource, IPath> res : resources.entrySet()) {
if (isBackendCompilableResource(res.getKey(), middleends))
resNames.add (res.getValue().toOSString());
}
Map<Class<?>, Object> specificParams = new HashMap<Class<?>, Object>();
Set<String> btsQualifiers = new HashSet<String> ();
configureMiddleends (jp, specificParams, btsQualifiers);
BackendTypesystem bts = CompositeTypesystemFactory.INSTANCE.createTypesystem (btsQualifiers);
if (!resNames.isEmpty()) {
compile (progress, resNames, specificParams, bts, defaultCharset);
}
} catch (CoreException e) {
_log.error ("Error doing full build ", e);
} catch (Exception e) {
_log.error (e);
}
}
private void doIncrementalBuild(IResourceDelta delta, SubMonitor monitor) throws CoreException {
final BackendDeltaVisitor visitor = new BackendDeltaVisitor (monitor);
delta.accept (visitor);
}
protected void onAddOrUpdate (final IFile resource, IProgressMonitor monitor) {
if (resource.exists()) {
final IProject project = getProject();
if (project != null) {
try {
final String charset = resource.getCharset();
final IJavaProject jp = JavaCore.create(getProject());
Map<Class<?>, Object> specificParams = new HashMap<Class<?>, Object>();
Set<String> btsQualifiers = new HashSet<String> ();
configureMiddleends (jp, specificParams, btsQualifiers);
BackendTypesystem bts = CompositeTypesystemFactory.INSTANCE.createTypesystem (btsQualifiers);
IPath resPath = null;
final IPackageFragmentRoot[] roots = jp.getPackageFragmentRoots();
for (int i = 0; i < roots.length; i++) {
if (roots[i].getResource() != null && ! roots[i].isArchive()) {
IResource packageRoot = roots[i].getResource();
if (resource.getRawLocation().toOSString().startsWith(packageRoot.getRawLocation().toOSString())) {
resPath = resource.getFullPath().makeRelativeTo(packageRoot.getFullPath());
break;
}
}
}
if (resPath != null) {
final Collection<String> resNames = Arrays.asList(resPath.toOSString());
if (!resNames .isEmpty()) {
if (charset != null)
compile (SubMonitor.convert (monitor, 1), resNames, specificParams, bts, charset);
else
compile (SubMonitor.convert (monitor, 1), resNames, specificParams, bts, getProject().getDefaultCharset());
}
}
} catch (CoreException e) {
_log.error("Error building "+ resource.getLocation().toOSString() + " incrementally", e);
}
}
}
}
protected void onRemoval (final IFile resource, IProgressMonitor monitor) {
}
private void compile (SubMonitor progress, Collection<String> resNames,
Map<Class<?>, Object> specificParams, BackendTypesystem bts, String fileEncoding)
throws CoreException {
BackendCompilerFacade compiler = new Java5CompilerFacade (bts);
final String middleEndPackage = "org.example";
final String middleEndName = getMiddleendName();
final String outputDir = getOutputFolder (progress).getRawLocation().toOSString();
_log.info ("Compiling M2T Backend executable resources " + resNames.toString());
compiler.compile (resNames, middleEndPackage, middleEndName, outputDir, specificParams, fileEncoding);
}
private void configureMiddleends (final IJavaProject project,
Map<Class<?>, Object> specificParams, Set<String> btsQualifiers) {
List<LanguageSpecificMiddleEndConfigurer> configurers = Activator.getInstance().getMiddleEndConfigurers();
for (LanguageSpecificMiddleEndConfigurer c : configurers) {
c.getMiddleEndName();
Map<Class<?>, Object> params = c.getSpecificParams (project);
specificParams.putAll (params);
btsQualifiers.addAll (c.getConfiguredTypeSystems (project));
}
}
private MiddleEnd createMiddleEnd () {
final IJavaProject jp = JavaCore.create(getProject());
Map<Class<?>, Object> specificParams = new HashMap<Class<?>, Object>();
Set<String> btsQualifiers = new HashSet<String> ();
configureMiddleends (jp, specificParams, btsQualifiers);
BackendTypesystem bts = CompositeTypesystemFactory.INSTANCE.createTypesystem (btsQualifiers);
if (MiddleEndFactory.canCreateFromExtentions()) {
return MiddleEndFactory.createFromExtensions(bts, specificParams);
}
return null;
}
private String getMiddleendName() {
return NamingHelper.toMiddleEndClassName (getProject ().getName(), null);
}
private void getAllResources (IPackageFragmentRoot root, IResource res, Map<IResource, IPath> resources) {
if (res instanceof IFile && ! res.isHidden()) {
IPath filePath = res.getFullPath();
IPath srcRootPath = root.getResource().getFullPath();
IPath relFilePath = filePath.makeRelativeTo (srcRootPath);
resources.put (res, relFilePath);
} else if (res instanceof IFolder) {
try {
for (IResource r : ((IFolder) res).members()) {
getAllResources(root, r, resources);
}
} catch (CoreException e) {
_log.error ("Error collecting resources for full build", e);
} catch (Exception e) {
_log.error ("Error collecting resources for full build", e);
}
}
}
private IFolder getOutputFolder(SubMonitor monitor) throws CoreException {
IFolder genFolder = getProject().getFolder (BACKEND_GEN_FOLDER);
if (! genFolder.exists())
genFolder.create(true, true, monitor);
return genFolder;
}
boolean isOnJavaClassPath(final IResource resource) {
final IJavaProject jp = JavaCore.create(resource.getProject());
if (jp != null)
return jp.isOnClasspath(resource);
return false;
}
private boolean isBackendCompilableResource(IResource resource, List<LanguageSpecificMiddleEnd> middleends) {
final String resourcePath = resource.getLocation().toOSString ();
if (resource instanceof IFile &&
!(resourcePath.endsWith (".class") || resourcePath.endsWith(".java")) &&
isOnJavaClassPath (resource))
{
for (LanguageSpecificMiddleEnd me : middleends) {
boolean isCanditate = me.mayHandle (resourcePath);
if (isCanditate)
return true;
}
return false;
} else {
return false;
}
}
protected URL[] getBuildClasspath (IJavaProject jp) {
try {
List<String> srcFolders = getSourceFolders(jp.getProject(), new LinkedList<String> ());
URL[] srcUrls = getbuildClasspathUrls(srcFolders);
return srcUrls;
} catch (CoreException e) {
_log.error (e);
}
return null;
}
private List<String> getSourceFolders (IProject project, List<String> srcFolders) throws CoreException {
IJavaProject jp = JavaCore.create (project);
final IPackageFragmentRoot[] roots = jp.getPackageFragmentRoots();
for (IPackageFragmentRoot iPackageFragmentRoot : roots) {
if (! iPackageFragmentRoot.getPath().equals (jp.getProject().getFolder (BackendBuilder.BACKEND_GEN_FOLDER))) {
if (iPackageFragmentRoot.getResource() != null && ! iPackageFragmentRoot.isArchive()) {
srcFolders.add (iPackageFragmentRoot.getResource().getRawLocation().toOSString());
}
}
}
for (IProject refProject : project.getReferencedProjects()) {
getSourceFolders (refProject, srcFolders);
}
return srcFolders;
}
private URL[] getbuildClasspathUrls(List<String> srcFolders) {
URL[] srcUrls = new URL[srcFolders.size()];
for (int i = 0; i < srcFolders.size(); i++) {
StringBuilder urlBuilder = new StringBuilder("file://");
urlBuilder.append(srcFolders.get(i));
urlBuilder.append(File.separator);
try {
URL currUrl = new URL (urlBuilder.toString());
srcUrls[i] = currUrl;
} catch (MalformedURLException e) {
_log.error(e);
}
}
return srcUrls;
}
}