ccw.builder.ClojureBuilder.java Source code

Java tutorial

Introduction

Here is the source code for ccw.builder.ClojureBuilder.java

Source

/*******************************************************************************
 * Copyright (c) 2009 Anyware Technologies 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:
 *     Gaetan Morice (Anyware Technologies) - initial implementation
 *     Laurent Petit (ccw) - adaptation to ccw
 *******************************************************************************/

package ccw.builder;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

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.IncrementalProjectBuilder;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.SubProgressMonitor;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaCore;

import ccw.CCWPlugin;
import ccw.launching.ClojureLaunchDelegate;
import ccw.repl.REPLView;

/*
 * gaetan.morice:
 * "
 * Note that this code is just a prototype and there is still lots of problems to fix. Among then :
 * Incremental build : I only implement full build.
 *  Synchronization with JDT build : as clojure and java files could depend on each others, the two builders
 *  need to be launch several time to resolve all the dependencies. 
 */
public class ClojureBuilder extends IncrementalProjectBuilder {
    public static final String CLOJURE_COMPILER_PROBLEM_MARKER_TYPE = "ccw.markers.problemmarkers.compilation";

    static public final String BUILDER_ID = "ccw.builder";

    @SuppressWarnings("unchecked")
    @Override
    protected IProject[] build(int kind, Map args, IProgressMonitor monitor) throws CoreException {

        if (!CCWPlugin.isAutoReloadOnStartupSaveEnabled()) {
            return null;
        }

        //       System.out.println("clojure build required");
        if (getProject() == null) {
            return null;
        }

        if (kind == AUTO_BUILD || kind == INCREMENTAL_BUILD) {
            if (onlyClassesOrOutputFolderRelatedDelta() && !onlyProjectTouched()) {
                //             System.out.println("nothing to build (onlyClassesOrOutputFolderRelatedDelta()=" + onlyClassesOrOutputFolderRelatedDelta()
                //                   + ", !onlyProjectTouched()=" + !onlyProjectTouched());
                return null;
            }
        }

        fullBuild(getProject(), monitor);
        return null;
    }

    /** Only project touch is treated similarly to a full build request */
    private boolean onlyProjectTouched() {
        IResourceDelta delta = getDelta(getProject());
        return delta.getResource().equals(getProject()) && delta.getAffectedChildren().length == 0;
    }

    private boolean onlyClassesOrOutputFolderRelatedDelta() throws CoreException {
        if (getDelta(getProject()) == null) {
            return false;
        }

        List<IPath> folders = new ArrayList<IPath>();
        folders.add(getClassesFolder(getProject()).getFullPath());
        for (IFolder outputPath : getSrcFolders(getProject()).values()) {
            folders.add(outputPath.getFullPath());
        }
        folders.add(getProject().getFolder(".settings").getFullPath());

        delta_loop: for (IResourceDelta d : getDelta(getProject()).getAffectedChildren()) {
            for (IPath folder : folders) {
                if (folder.isPrefixOf(d.getFullPath())) {
                    continue delta_loop;
                }
            }
            //         System.out.println("affected children for build:" + d.getFullPath());
            return false;
        }
        return true;
    }

    protected void incrementalBuild(IResourceDelta delta, IProgressMonitor monitor) {
        // TODO Auto-generated method stub

    }

    public static void fullBuild(IProject project, IProgressMonitor monitor) throws CoreException {

        if (monitor == null) {
            monitor = new NullProgressMonitor();
        }

        createClassesFolder(project, new SubProgressMonitor(monitor, 0));

        // Issue #203 is probably related to the following way of getting a REPLView.
        // A race condition between the builder and the Eclipse machinery creating the views, etc.
        // We will probably have to refactor stuff to separate things a little bit more, but for the time
        // being, as an experiment and hopefully a temporary patch for the problem, I'll just add a little bit
        // delay in the build:

        // TODO see if we can do something more clever than having to use the UI thread and get a View object ...
        REPLView repl = CCWPlugin.getDefault().getProjectREPL(project);
        if (repl == null) {
            // Another chance, to fight with a hack, temporarily at least, the race condition
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            repl = CCWPlugin.getDefault().getProjectREPL(project);
        }
        if (repl == null || repl.isDisposed() || !ClojureLaunchDelegate.isAutoReloadEnabled(repl.getLaunch())) {
            //           System.out.println("REPL not found, or disposed, or autoReloadEnabled not found on launch configuration");
            return;
        }

        deleteMarkers(project);

        ClojureVisitor visitor = new ClojureVisitor(repl.getSafeToolingConnection());
        visitor.visit(getSrcFolders(project));

        getClassesFolder(project).refreshLocal(IResource.DEPTH_INFINITE, new SubProgressMonitor(monitor, 0));
    }

    private static IFolder getClassesFolder(IProject project) {
        return project.getFolder("classes");
    }

    private static Map<IFolder, IFolder> getSrcFolders(IProject project) throws CoreException {
        Map<IFolder, IFolder> srcFolders = new HashMap<IFolder, IFolder>();

        IJavaProject jProject = JavaCore.create(project);
        IClasspathEntry[] entries = jProject.getResolvedClasspath(true);
        IPath defaultOutputFolder = jProject.getOutputLocation();
        for (IClasspathEntry entry : entries) {
            switch (entry.getEntryKind()) {
            case IClasspathEntry.CPE_SOURCE:
                IFolder folder = project.getWorkspace().getRoot().getFolder(entry.getPath());
                IFolder outputFolder = project.getWorkspace().getRoot().getFolder(
                        (entry.getOutputLocation() == null) ? defaultOutputFolder : entry.getOutputLocation());
                if (folder.exists())
                    srcFolders.put(folder, outputFolder);
                break;
            case IClasspathEntry.CPE_LIBRARY:
                break;
            case IClasspathEntry.CPE_PROJECT:
                // TODO should compile here ?
                break;
            case IClasspathEntry.CPE_CONTAINER:
            case IClasspathEntry.CPE_VARIABLE:
                // Impossible cases, since entries are resolved
            default:
                break;
            }
        }
        return srcFolders;
    }

    private static void createClassesFolder(IProject project, IProgressMonitor monitor) throws CoreException {
        if (!getClassesFolder(project).exists()) {
            getClassesFolder(project).create(true, true, monitor);
        }
    }

    @Override
    protected void clean(IProgressMonitor monitor) throws CoreException {
        if (monitor == null) {
            monitor = new NullProgressMonitor();
        }

        try {
            if (CCWPlugin.isAutoReloadOnStartupSaveEnabled()) {
                // Only when autoReloadOnStartupSave is enabled do we have AOT compilation
                //active and a classes/ folder to manage
                getClassesFolder(getProject()).delete(true, new SubProgressMonitor(monitor, 0));
                createClassesFolder(getProject(), new SubProgressMonitor(monitor, 0));
                getClassesFolder(getProject()).refreshLocal(IResource.DEPTH_INFINITE,
                        new SubProgressMonitor(monitor, 0));
                getClassesFolder(getProject()).setDerived(true); //, monitor); removed monitor argument, probably a 3.5/3.6 only stuff
            }
        } catch (CoreException e) {
            CCWPlugin.logError("Unable to correctly clean classes folder", e);
        }

        deleteMarkers(getProject());
    }

    private static void deleteMarkers(IProject project) throws CoreException {
        for (IFolder srcFolder : getSrcFolders(project).keySet()) {
            srcFolder.deleteMarkers(CLOJURE_COMPILER_PROBLEM_MARKER_TYPE, true, IFile.DEPTH_INFINITE);
        }
    }

}