net.sf.j2s.core.builder.JavaBuilder.java Source code

Java tutorial

Introduction

Here is the source code for net.sf.j2s.core.builder.JavaBuilder.java

Source

/*******************************************************************************
 * Copyright (c) 2000, 2013 IBM Corporation 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:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package net.sf.j2s.core.builder;

import org.eclipse.core.resources.*;
import org.eclipse.core.runtime.*;
import org.eclipse.jdt.core.*;
import org.eclipse.jdt.core.compiler.*;
import org.eclipse.jdt.internal.compiler.util.SimpleLookupTable;
import org.eclipse.jdt.internal.core.*;
import org.eclipse.jdt.internal.core.util.Messages;
import org.eclipse.jdt.internal.core.util.Util;

import java.io.*;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;

public class JavaBuilder extends IncrementalProjectBuilder {

    IProject currentProject;
    JavaProject javaProject;
    IWorkspaceRoot workspaceRoot;
    CompilationParticipant[] participants;
    NameEnvironment nameEnvironment;
    SimpleLookupTable binaryLocationsPerProject; // maps a project to its binary resources (output folders, class folders, zip/jar files)
    public State lastState;
    BuildNotifier notifier;
    char[][] extraResourceFileFilters;
    String[] extraResourceFolderFilters;
    public static final String SOURCE_ID = "JDT"; //$NON-NLS-1$

    public static boolean DEBUG = false;
    public static boolean SHOW_STATS = false;

    /**
     * A list of project names that have been built.
     * This list is used to reset the JavaModel.existingExternalFiles cache when a build cycle begins
     * so that deleted external jars are discovered.
     */
    static ArrayList builtProjects = null;

    public static IMarker[] getProblemsFor(IResource resource) {
        try {
            if (resource != null && resource.exists()) {
                IMarker[] markers = resource.findMarkers(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER, false,
                        IResource.DEPTH_INFINITE);
                Set markerTypes = JavaModelManager.getJavaModelManager().compilationParticipants
                        .managedMarkerTypes();
                if (markerTypes.isEmpty())
                    return markers;
                ArrayList markerList = new ArrayList(5);
                for (int i = 0, length = markers.length; i < length; i++) {
                    markerList.add(markers[i]);
                }
                Iterator iterator = markerTypes.iterator();
                while (iterator.hasNext()) {
                    markers = resource.findMarkers((String) iterator.next(), false, IResource.DEPTH_INFINITE);
                    for (int i = 0, length = markers.length; i < length; i++) {
                        markerList.add(markers[i]);
                    }
                }
                IMarker[] result;
                markerList.toArray(result = new IMarker[markerList.size()]);
                return result;
            }
        } catch (CoreException e) {
            // assume there are no problems
        }
        return new IMarker[0];
    }

    public static IMarker[] getTasksFor(IResource resource) {
        try {
            if (resource != null && resource.exists())
                return resource.findMarkers(IJavaModelMarker.TASK_MARKER, false, IResource.DEPTH_INFINITE);
        } catch (CoreException e) {
            // assume there are no tasks
        }
        return new IMarker[0];
    }

    /**
     * Hook allowing to initialize some static state before a complete build iteration.
     * This hook is invoked during PRE_AUTO_BUILD notification
     */
    public static void buildStarting() {
        // build is about to start
    }

    /**
     * Hook allowing to reset some static state after a complete build iteration.
     * This hook is invoked during POST_AUTO_BUILD notification
     */
    public static void buildFinished() {
        BuildNotifier.resetProblemCounters();
    }

    public static void removeProblemsFor(IResource resource) {
        try {
            if (resource != null && resource.exists()) {
                resource.deleteMarkers(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER, false, IResource.DEPTH_INFINITE);

                // delete managed markers
                Set markerTypes = JavaModelManager.getJavaModelManager().compilationParticipants
                        .managedMarkerTypes();
                if (markerTypes.size() == 0)
                    return;
                Iterator iterator = markerTypes.iterator();
                while (iterator.hasNext())
                    resource.deleteMarkers((String) iterator.next(), false, IResource.DEPTH_INFINITE);
            }
        } catch (CoreException e) {
            // assume there were no problems
        }
    }

    public static void removeTasksFor(IResource resource) {
        try {
            if (resource != null && resource.exists())
                resource.deleteMarkers(IJavaModelMarker.TASK_MARKER, false, IResource.DEPTH_INFINITE);
        } catch (CoreException e) {
            // assume there were no problems
        }
    }

    public static void removeProblemsAndTasksFor(IResource resource) {
        try {
            if (resource != null && resource.exists()) {
                resource.deleteMarkers(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER, false, IResource.DEPTH_INFINITE);
                resource.deleteMarkers(IJavaModelMarker.TASK_MARKER, false, IResource.DEPTH_INFINITE);

                // delete managed markers
                Set markerTypes = JavaModelManager.getJavaModelManager().compilationParticipants
                        .managedMarkerTypes();
                if (markerTypes.size() == 0)
                    return;
                Iterator iterator = markerTypes.iterator();
                while (iterator.hasNext())
                    resource.deleteMarkers((String) iterator.next(), false, IResource.DEPTH_INFINITE);
            }
        } catch (CoreException e) {
            // assume there were no problems
        }
    }

    public static State readState(IProject project, DataInputStream in) throws IOException {
        return State.read(project, in);
    }

    public static void writeState(Object state, DataOutputStream out) throws IOException {
        ((State) state).write(out);
    }

    protected IProject[] build(int kind, Map ignored, IProgressMonitor monitor) throws CoreException {
        this.currentProject = getProject();
        if (this.currentProject == null || !this.currentProject.isAccessible())
            return new IProject[0];

        if (DEBUG)
            System.out.println("\nJavaBuilder: Starting build of " + this.currentProject.getName() //$NON-NLS-1$
                    + " @ " + new Date(System.currentTimeMillis())); //$NON-NLS-1$
        this.notifier = new BuildNotifier(monitor, this.currentProject);
        this.notifier.begin();
        boolean ok = false;
        try {
            this.notifier.checkCancel();
            kind = initializeBuilder(kind, true);

            if (isWorthBuilding()) {
                if (kind == FULL_BUILD) {
                    if (DEBUG)
                        System.out.println("JavaBuilder: Performing full build as requested"); //$NON-NLS-1$
                    buildAll();
                } else {
                    if ((this.lastState = getLastState(this.currentProject)) == null) {
                        if (DEBUG)
                            System.out.println(
                                    "JavaBuilder: Performing full build since last saved state was not found"); //$NON-NLS-1$
                        buildAll();
                    } else if (hasClasspathChanged()) {
                        // if the output location changes, do not delete the binary files from old location
                        // the user may be trying something
                        if (DEBUG)
                            System.out.println("JavaBuilder: Performing full build since classpath has changed"); //$NON-NLS-1$
                        buildAll();
                    } else if (this.nameEnvironment.sourceLocations.length > 0) {
                        // if there is no source to compile & no classpath changes then we are done
                        SimpleLookupTable deltas = findDeltas();
                        if (deltas == null) {
                            if (DEBUG)
                                System.out.println(
                                        "JavaBuilder: Performing full build since deltas are missing after incremental request"); //$NON-NLS-1$
                            buildAll();
                        } else if (deltas.elementSize > 0) {
                            buildDeltas(deltas);
                        } else if (DEBUG) {
                            System.out.println("JavaBuilder: Nothing to build since deltas were empty"); //$NON-NLS-1$
                        }
                    } else {
                        if (hasStructuralDelta()) { // double check that a jar file didn't get replaced in a binary project
                            if (DEBUG)
                                System.out.println(
                                        "JavaBuilder: Performing full build since there are structural deltas"); //$NON-NLS-1$
                            buildAll();
                        } else {
                            if (DEBUG)
                                System.out.println(
                                        "JavaBuilder: Nothing to build since there are no source folders and no deltas"); //$NON-NLS-1$
                            this.lastState.tagAsNoopBuild();
                        }
                    }
                }
                ok = true;
            }
        } catch (CoreException e) {
            Util.log(e, "JavaBuilder handling CoreException while building: " + this.currentProject.getName()); //$NON-NLS-1$
            createInconsistentBuildMarker(e);
        } catch (ImageBuilderInternalException e) {
            Util.log(e.getThrowable(), "JavaBuilder handling ImageBuilderInternalException while building: " //$NON-NLS-1$
                    + this.currentProject.getName());
            createInconsistentBuildMarker(e.coreException);
        } catch (MissingSourceFileException e) {
            // do not log this exception since its thrown to handle aborted compiles because of missing source files
            if (DEBUG)
                System.out.println(Messages.bind(Messages.build_missingSourceFile, e.missingSourceFile));
            removeProblemsAndTasksFor(this.currentProject); // make this the only problem for this project
            IMarker marker = this.currentProject.createMarker(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER);
            marker.setAttributes(new String[] { IMarker.MESSAGE, IMarker.SEVERITY, IMarker.SOURCE_ID },
                    new Object[] { Messages.bind(Messages.build_missingSourceFile, e.missingSourceFile),
                            new Integer(IMarker.SEVERITY_ERROR), JavaBuilder.SOURCE_ID });
        } finally {
            for (int i = 0, l = this.participants == null ? 0 : this.participants.length; i < l; i++)
                this.participants[i].buildFinished(this.javaProject);
            if (!ok)
                // If the build failed, clear the previously built state, forcing a full build next time.
                clearLastState();
            this.notifier.done();
            cleanup();
        }
        IProject[] requiredProjects = getRequiredProjects(true);
        if (DEBUG)
            System.out.println("JavaBuilder: Finished build of " + this.currentProject.getName() //$NON-NLS-1$
                    + " @ " + new Date(System.currentTimeMillis()) + "\n"); //$NON-NLS-1$ //$NON-NLS-2$
        return requiredProjects;
    }

    private void buildAll() {
        this.notifier.checkCancel();
        this.notifier.subTask(Messages.bind(Messages.build_preparingBuild, this.currentProject.getName()));
        if (DEBUG && this.lastState != null)
            System.out.println("JavaBuilder: Clearing last state : " + this.lastState); //$NON-NLS-1$
        clearLastState();
        BatchImageBuilder imageBuilder = new Java2ScriptBatchImageBuilder(this, true);
        imageBuilder.build();
        recordNewState(imageBuilder.newState);
    }

    private void buildDeltas(SimpleLookupTable deltas) {
        this.notifier.checkCancel();
        this.notifier.subTask(Messages.bind(Messages.build_preparingBuild, this.currentProject.getName()));
        if (DEBUG && this.lastState != null)
            System.out.println("JavaBuilder: Clearing last state : " + this.lastState); //$NON-NLS-1$
        clearLastState(); // clear the previously built state so if the build fails, a full build will occur next time
        IncrementalImageBuilder imageBuilder = new Java2ScriptIncrementalImageBuilder(this);
        if (imageBuilder.build(deltas)) {
            recordNewState(imageBuilder.newState);
        } else {
            if (DEBUG)
                System.out.println("JavaBuilder: Performing full build since incremental build failed"); //$NON-NLS-1$
            buildAll();
        }
    }

    protected void clean(IProgressMonitor monitor) throws CoreException {
        this.currentProject = getProject();
        if (this.currentProject == null || !this.currentProject.isAccessible())
            return;

        if (DEBUG)
            System.out.println("\nJavaBuilder: Cleaning " + this.currentProject.getName() //$NON-NLS-1$
                    + " @ " + new Date(System.currentTimeMillis())); //$NON-NLS-1$
        this.notifier = new BuildNotifier(monitor, this.currentProject);
        this.notifier.begin();
        try {
            this.notifier.checkCancel();

            initializeBuilder(CLEAN_BUILD, true);
            if (DEBUG)
                System.out.println("JavaBuilder: Clearing last state as part of clean : " + this.lastState); //$NON-NLS-1$
            clearLastState();
            removeProblemsAndTasksFor(this.currentProject);
            new BatchImageBuilder(this, false).cleanOutputFolders(false);
        } catch (CoreException e) {
            Util.log(e, "JavaBuilder handling CoreException while cleaning: " + this.currentProject.getName()); //$NON-NLS-1$
            createInconsistentBuildMarker(e);
        } finally {
            this.notifier.done();
            cleanup();
        }
        if (DEBUG)
            System.out.println("JavaBuilder: Finished cleaning " + this.currentProject.getName() //$NON-NLS-1$
                    + " @ " + new Date(System.currentTimeMillis())); //$NON-NLS-1$
    }

    private void createInconsistentBuildMarker(CoreException coreException) throws CoreException {
        String message = null;
        IStatus status = coreException.getStatus();
        if (status.isMultiStatus()) {
            IStatus[] children = status.getChildren();
            if (children != null && children.length > 0)
                message = children[0].getMessage();
        }
        if (message == null)
            message = coreException.getMessage();

        IMarker marker = this.currentProject.createMarker(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER);
        marker.setAttributes(
                new String[] { IMarker.MESSAGE, IMarker.SEVERITY, IJavaModelMarker.CATEGORY_ID, IMarker.SOURCE_ID },
                new Object[] { Messages.bind(Messages.build_inconsistentProject, message),
                        new Integer(IMarker.SEVERITY_ERROR), new Integer(CategorizedProblem.CAT_BUILDPATH),
                        JavaBuilder.SOURCE_ID });
    }

    private void cleanup() {
        this.participants = null;
        this.nameEnvironment = null;
        this.binaryLocationsPerProject = null;
        this.lastState = null;
        this.notifier = null;
        this.extraResourceFileFilters = null;
        this.extraResourceFolderFilters = null;
    }

    private void clearLastState() {
        JavaModelManager.getJavaModelManager().setLastBuiltState(this.currentProject, null);
    }

    boolean filterExtraResource(IResource resource) {
        if (this.extraResourceFileFilters != null) {
            char[] name = resource.getName().toCharArray();
            for (int i = 0, l = this.extraResourceFileFilters.length; i < l; i++)
                if (CharOperation.match(this.extraResourceFileFilters[i], name, true))
                    return true;
        }
        if (this.extraResourceFolderFilters != null) {
            IPath path = resource.getProjectRelativePath();
            String pathName = path.toString();
            int count = path.segmentCount();
            if (resource.getType() == IResource.FILE)
                count--;
            for (int i = 0, l = this.extraResourceFolderFilters.length; i < l; i++)
                if (pathName.indexOf(this.extraResourceFolderFilters[i]) != -1)
                    for (int j = 0; j < count; j++)
                        if (this.extraResourceFolderFilters[i].equals(path.segment(j)))
                            return true;
        }
        return false;
    }

    private SimpleLookupTable findDeltas() {
        this.notifier.subTask(Messages.bind(Messages.build_readingDelta, this.currentProject.getName()));
        IResourceDelta delta = getDelta(this.currentProject);
        SimpleLookupTable deltas = new SimpleLookupTable(3);
        if (delta != null) {
            if (delta.getKind() != IResourceDelta.NO_CHANGE) {
                if (DEBUG)
                    System.out.println("JavaBuilder: Found source delta for: " + this.currentProject.getName()); //$NON-NLS-1$
                deltas.put(this.currentProject, delta);
            }
        } else {
            if (DEBUG)
                System.out.println("JavaBuilder: Missing delta for: " + this.currentProject.getName()); //$NON-NLS-1$
            this.notifier.subTask(""); //$NON-NLS-1$
            return null;
        }

        Object[] keyTable = this.binaryLocationsPerProject.keyTable;
        Object[] valueTable = this.binaryLocationsPerProject.valueTable;
        nextProject: for (int i = 0, l = keyTable.length; i < l; i++) {
            IProject p = (IProject) keyTable[i];
            if (p != null && p != this.currentProject) {
                State s = getLastState(p);
                if (!this.lastState.wasStructurallyChanged(p, s)) { // see if we can skip its delta
                    if (s.wasNoopBuild())
                        continue nextProject; // project has no source folders and can be skipped
                    ClasspathLocation[] classFoldersAndJars = (ClasspathLocation[]) valueTable[i];
                    boolean canSkip = true;
                    for (int j = 0, m = classFoldersAndJars.length; j < m; j++) {
                        if (classFoldersAndJars[j].isOutputFolder())
                            classFoldersAndJars[j] = null; // can ignore output folder since project was not structurally changed
                        else
                            canSkip = false;
                    }
                    if (canSkip)
                        continue nextProject; // project has no structural changes in its output folders
                }

                this.notifier.subTask(Messages.bind(Messages.build_readingDelta, p.getName()));
                delta = getDelta(p);
                if (delta != null) {
                    if (delta.getKind() != IResourceDelta.NO_CHANGE) {
                        if (DEBUG)
                            System.out.println("JavaBuilder: Found binary delta for: " + p.getName()); //$NON-NLS-1$
                        deltas.put(p, delta);
                    }
                } else {
                    if (DEBUG)
                        System.out.println("JavaBuilder: Missing delta for: " + p.getName()); //$NON-NLS-1$
                    this.notifier.subTask(""); //$NON-NLS-1$
                    return null;
                }
            }
        }
        this.notifier.subTask(""); //$NON-NLS-1$
        return deltas;
    }

    public State getLastState(IProject project) {
        org.eclipse.jdt.internal.core.builder.State lastBuiltState = (org.eclipse.jdt.internal.core.builder.State) JavaModelManager
                .getJavaModelManager().getLastBuiltState(project, this.notifier.monitor);
        if (lastBuiltState == null) {
            return null;
        }
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        DataOutputStream out = new DataOutputStream(baos);
        try {
            org.eclipse.jdt.internal.core.builder.JavaBuilder.writeState(lastBuiltState, out);
        } catch (IOException e) {
            e.printStackTrace();
        }

        ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
        DataInputStream in = new DataInputStream(bais);
        State state = null;
        try {
            state = State.read(project, in);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return state;
    }

    /* Return the list of projects for which it requires a resource delta. This builder's project
    * is implicitly included and need not be specified. Builders must re-specify the list
    * of interesting projects every time they are run as this is not carried forward
    * beyond the next build. Missing projects should be specified but will be ignored until
    * they are added to the workspace.
    */
    private IProject[] getRequiredProjects(boolean includeBinaryPrerequisites) {
        if (this.javaProject == null || this.workspaceRoot == null)
            return new IProject[0];

        ArrayList projects = new ArrayList();
        ExternalFoldersManager externalFoldersManager = JavaModelManager.getExternalManager();
        try {
            IClasspathEntry[] entries = this.javaProject.getExpandedClasspath();
            for (int i = 0, l = entries.length; i < l; i++) {
                IClasspathEntry entry = entries[i];
                IPath path = entry.getPath();
                IProject p = null;
                switch (entry.getEntryKind()) {
                case IClasspathEntry.CPE_PROJECT:
                    p = this.workspaceRoot.getProject(path.lastSegment()); // missing projects are considered too
                    if (((ClasspathEntry) entry).isOptional() && !JavaProject.hasJavaNature(p)) // except if entry is optional
                        p = null;
                    break;
                case IClasspathEntry.CPE_LIBRARY:
                    if (includeBinaryPrerequisites && path.segmentCount() > 0) {
                        // some binary resources on the class path can come from projects that are not included in the project references
                        IResource resource = this.workspaceRoot.findMember(path.segment(0));
                        if (resource instanceof IProject) {
                            p = (IProject) resource;
                        } else {
                            resource = externalFoldersManager.getFolder(path);
                            if (resource != null)
                                p = resource.getProject();
                        }
                    }
                }
                if (p != null && !projects.contains(p))
                    projects.add(p);
            }
        } catch (JavaModelException e) {
            return new IProject[0];
        }
        IProject[] result = new IProject[projects.size()];
        projects.toArray(result);
        return result;
    }

    boolean hasBuildpathErrors() throws CoreException {
        IMarker[] markers = this.currentProject.findMarkers(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER, false,
                IResource.DEPTH_ZERO);
        for (int i = 0, l = markers.length; i < l; i++)
            if (markers[i].getAttribute(IJavaModelMarker.CATEGORY_ID, -1) == CategorizedProblem.CAT_BUILDPATH)
                return true;
        return false;
    }

    private boolean hasClasspathChanged() {
        ClasspathMultiDirectory[] newSourceLocations = this.nameEnvironment.sourceLocations;
        ClasspathMultiDirectory[] oldSourceLocations = this.lastState.sourceLocations;
        int newLength = newSourceLocations.length;
        int oldLength = oldSourceLocations.length;
        int n, o;
        for (n = o = 0; n < newLength && o < oldLength; n++, o++) {
            if (newSourceLocations[n].equals(oldSourceLocations[o]))
                continue; // checks source & output folders
            try {
                if (newSourceLocations[n].sourceFolder.members().length == 0) { // added new empty source folder
                    o--;
                    continue;
                } else if (this.lastState.isSourceFolderEmpty(oldSourceLocations[o].sourceFolder)) {
                    n--;
                    continue;
                }
            } catch (CoreException ignore) { // skip it
            }
            if (DEBUG) {
                System.out.println("JavaBuilder: New location: " + newSourceLocations[n] + "\n!= old location: " //$NON-NLS-1$//$NON-NLS-2$
                        + oldSourceLocations[o]);
                printLocations(newSourceLocations, oldSourceLocations);
            }
            return true;
        }
        while (n < newLength) {
            try {
                if (newSourceLocations[n].sourceFolder.members().length == 0) { // added new empty source folder
                    n++;
                    continue;
                }
            } catch (CoreException ignore) { // skip it
            }
            if (DEBUG) {
                System.out.println("JavaBuilder: Added non-empty source folder"); //$NON-NLS-1$
                printLocations(newSourceLocations, oldSourceLocations);
            }
            return true;
        }
        while (o < oldLength) {
            if (this.lastState.isSourceFolderEmpty(oldSourceLocations[o].sourceFolder)) {
                o++;
                continue;
            }
            if (DEBUG) {
                System.out.println("JavaBuilder: Removed non-empty source folder"); //$NON-NLS-1$
                printLocations(newSourceLocations, oldSourceLocations);
            }
            return true;
        }

        ClasspathLocation[] newBinaryLocations = this.nameEnvironment.binaryLocations;
        ClasspathLocation[] oldBinaryLocations = this.lastState.binaryLocations;
        newLength = newBinaryLocations.length;
        oldLength = oldBinaryLocations.length;
        for (n = o = 0; n < newLength && o < oldLength; n++, o++) {
            if (newBinaryLocations[n].equals(oldBinaryLocations[o]))
                continue;
            if (DEBUG) {
                System.out.println("JavaBuilder: New location: " + newBinaryLocations[n] + "\n!= old location: " //$NON-NLS-1$//$NON-NLS-2$
                        + oldBinaryLocations[o]);
                printLocations(newBinaryLocations, oldBinaryLocations);
            }
            return true;
        }
        if (n < newLength || o < oldLength) {
            if (DEBUG) {
                System.out.println("JavaBuilder: Number of binary folders/jar files has changed:"); //$NON-NLS-1$
                printLocations(newBinaryLocations, oldBinaryLocations);
            }
            return true;
        }
        return false;
    }

    private boolean hasJavaBuilder(IProject project) throws CoreException {
        ICommand[] buildCommands = project.getDescription().getBuildSpec();
        for (int i = 0, l = buildCommands.length; i < l; i++)
            if (buildCommands[i].getBuilderName().equals(JavaCore.BUILDER_ID))
                return true;
        return false;
    }

    private boolean hasStructuralDelta() {
        // handle case when currentProject has only .class file folders and/or jar files... no source/output folders
        IResourceDelta delta = getDelta(this.currentProject);
        if (delta != null && delta.getKind() != IResourceDelta.NO_CHANGE) {
            ClasspathLocation[] classFoldersAndJars = (ClasspathLocation[]) this.binaryLocationsPerProject
                    .get(this.currentProject);
            if (classFoldersAndJars != null) {
                for (int i = 0, l = classFoldersAndJars.length; i < l; i++) {
                    ClasspathLocation classFolderOrJar = classFoldersAndJars[i]; // either a .class file folder or a zip/jar file
                    if (classFolderOrJar != null) {
                        IPath p = classFolderOrJar.getProjectRelativePath();
                        if (p != null) {
                            IResourceDelta binaryDelta = delta.findMember(p);
                            if (binaryDelta != null && binaryDelta.getKind() != IResourceDelta.NO_CHANGE)
                                return true;
                        }
                    }
                }
            }
        }
        return false;
    }

    private int initializeBuilder(int kind, boolean forBuild) throws CoreException {
        // some calls just need the nameEnvironment initialized so skip the rest
        this.javaProject = (JavaProject) JavaCore.create(this.currentProject);
        this.workspaceRoot = this.currentProject.getWorkspace().getRoot();

        if (forBuild) {
            // cache the known participants for this project
            this.participants = JavaModelManager.getJavaModelManager().compilationParticipants
                    .getCompilationParticipants(this.javaProject);
            if (this.participants != null)
                for (int i = 0, l = this.participants.length; i < l; i++)
                    if (this.participants[i]
                            .aboutToBuild(this.javaProject) == CompilationParticipant.NEEDS_FULL_BUILD)
                        kind = FULL_BUILD;

            // Flush the existing external files cache if this is the beginning of a build cycle
            String projectName = this.currentProject.getName();
            if (builtProjects == null || builtProjects.contains(projectName)) {
                try {
                    Method method = JavaModel.class.getMethod("flushExternalFileCache", new Class[] { Void.class });
                    if (method != null) {
                        method.invoke(JavaModel.class, new Object[0]);
                    }
                } catch (Throwable e) {
                    e.printStackTrace();
                }
                //JavaModel.flushExternalFileCache();
                builtProjects = new ArrayList();
            }
            builtProjects.add(projectName);
        }

        this.binaryLocationsPerProject = new SimpleLookupTable(3);
        this.nameEnvironment = new NameEnvironment(this.workspaceRoot, this.javaProject,
                this.binaryLocationsPerProject, this.notifier);

        if (forBuild) {
            String filterSequence = this.javaProject.getOption(JavaCore.CORE_JAVA_BUILD_RESOURCE_COPY_FILTER, true);
            char[][] filters = filterSequence != null && filterSequence.length() > 0
                    ? CharOperation.splitAndTrimOn(',', filterSequence.toCharArray())
                    : null;
            if (filters == null) {
                this.extraResourceFileFilters = null;
                this.extraResourceFolderFilters = null;
            } else {
                int fileCount = 0, folderCount = 0;
                for (int i = 0, l = filters.length; i < l; i++) {
                    char[] f = filters[i];
                    if (f.length == 0)
                        continue;
                    if (f[f.length - 1] == '/')
                        folderCount++;
                    else
                        fileCount++;
                }
                this.extraResourceFileFilters = new char[fileCount][];
                this.extraResourceFolderFilters = new String[folderCount];
                for (int i = 0, l = filters.length; i < l; i++) {
                    char[] f = filters[i];
                    if (f.length == 0)
                        continue;
                    if (f[f.length - 1] == '/')
                        this.extraResourceFolderFilters[--folderCount] = new String(f, 0, f.length - 1);
                    else
                        this.extraResourceFileFilters[--fileCount] = f;
                }
            }
        }
        return kind;
    }

    private boolean isClasspathBroken(IClasspathEntry[] classpath, IProject p) throws CoreException {
        IMarker[] markers = p.findMarkers(IJavaModelMarker.BUILDPATH_PROBLEM_MARKER, false, IResource.DEPTH_ZERO);
        for (int i = 0, l = markers.length; i < l; i++)
            if (markers[i].getAttribute(IMarker.SEVERITY, -1) == IMarker.SEVERITY_ERROR)
                return true;
        return false;
    }

    private boolean isWorthBuilding() throws CoreException {
        boolean abortBuilds = JavaCore.ABORT
                .equals(this.javaProject.getOption(JavaCore.CORE_JAVA_BUILD_INVALID_CLASSPATH, true));
        if (!abortBuilds)
            return true;

        // Abort build only if there are classpath errors
        if (isClasspathBroken(this.javaProject.getRawClasspath(), this.currentProject)) {
            if (DEBUG)
                System.out.println(
                        "JavaBuilder: Aborted build because project has classpath errors (incomplete or involved in cycle)"); //$NON-NLS-1$

            removeProblemsAndTasksFor(this.currentProject); // remove all compilation problems

            IMarker marker = this.currentProject.createMarker(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER);
            marker.setAttributes(
                    new String[] { IMarker.MESSAGE, IMarker.SEVERITY, IJavaModelMarker.CATEGORY_ID,
                            IMarker.SOURCE_ID },
                    new Object[] { Messages.build_abortDueToClasspathProblems, new Integer(IMarker.SEVERITY_ERROR),
                            new Integer(CategorizedProblem.CAT_BUILDPATH), JavaBuilder.SOURCE_ID });
            return false;
        }

        if (JavaCore.WARNING.equals(this.javaProject.getOption(JavaCore.CORE_INCOMPLETE_CLASSPATH, true)))
            return true;

        // make sure all prereq projects have valid build states... only when aborting builds since projects in cycles do not have build states
        // except for projects involved in a 'warning' cycle (see below)
        IProject[] requiredProjects = getRequiredProjects(false);
        for (int i = 0, l = requiredProjects.length; i < l; i++) {
            IProject p = requiredProjects[i];
            if (getLastState(p) == null) {
                // The prereq project has no build state: if this prereq project has a 'warning' cycle marker then allow build (see bug id 23357)
                JavaProject prereq = (JavaProject) JavaCore.create(p);
                if (prereq.hasCycleMarker() && JavaCore.WARNING
                        .equals(this.javaProject.getOption(JavaCore.CORE_CIRCULAR_CLASSPATH, true))) {
                    if (DEBUG)
                        System.out
                                .println("JavaBuilder: Continued to build even though prereq project " + p.getName() //$NON-NLS-1$
                                        + " was not built since its part of a cycle"); //$NON-NLS-1$
                    continue;
                }
                if (!hasJavaBuilder(p)) {
                    if (DEBUG)
                        System.out
                                .println("JavaBuilder: Continued to build even though prereq project " + p.getName() //$NON-NLS-1$
                                        + " is not built by JavaBuilder"); //$NON-NLS-1$
                    continue;
                }
                if (DEBUG)
                    System.out.println("JavaBuilder: Aborted build because prereq project " + p.getName() //$NON-NLS-1$
                            + " was not built"); //$NON-NLS-1$

                removeProblemsAndTasksFor(this.currentProject); // make this the only problem for this project
                IMarker marker = this.currentProject.createMarker(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER);
                marker.setAttributes(
                        new String[] { IMarker.MESSAGE, IMarker.SEVERITY, IJavaModelMarker.CATEGORY_ID,
                                IMarker.SOURCE_ID },
                        new Object[] {
                                isClasspathBroken(prereq.getRawClasspath(), p)
                                        ? Messages.bind(Messages.build_prereqProjectHasClasspathProblems,
                                                p.getName())
                                        : Messages.bind(Messages.build_prereqProjectMustBeRebuilt, p.getName()),
                                new Integer(IMarker.SEVERITY_ERROR), new Integer(CategorizedProblem.CAT_BUILDPATH),
                                JavaBuilder.SOURCE_ID });
                return false;
            }
        }
        return true;
    }

    /*
     * Instruct the build manager that this project is involved in a cycle and
     * needs to propagate structural changes to the other projects in the cycle.
     */
    void mustPropagateStructuralChanges() {
        LinkedHashSet cycleParticipants = new LinkedHashSet(3);
        this.javaProject.updateCycleParticipants(new ArrayList(), cycleParticipants, this.workspaceRoot,
                new HashSet(3), null);
        IPath currentPath = this.javaProject.getPath();
        Iterator i = cycleParticipants.iterator();
        while (i.hasNext()) {
            IPath participantPath = (IPath) i.next();
            if (participantPath != currentPath) {
                IProject project = this.workspaceRoot.getProject(participantPath.segment(0));
                if (hasBeenBuilt(project)) {
                    if (DEBUG)
                        System.out
                                .println("JavaBuilder: Requesting another build iteration since cycle participant " //$NON-NLS-1$
                                        + project.getName() + " has not yet seen some structural changes"); //$NON-NLS-1$
                    needRebuild();
                    return;
                }
            }
        }
    }

    private void printLocations(ClasspathLocation[] newLocations, ClasspathLocation[] oldLocations) {
        System.out.println("JavaBuilder: New locations:"); //$NON-NLS-1$
        for (int i = 0, length = newLocations.length; i < length; i++)
            System.out.println("    " + newLocations[i].debugPathString()); //$NON-NLS-1$
        System.out.println("JavaBuilder: Old locations:"); //$NON-NLS-1$
        for (int i = 0, length = oldLocations.length; i < length; i++)
            System.out.println("    " + oldLocations[i].debugPathString()); //$NON-NLS-1$
    }

    private void recordNewState(State state) {
        Object[] keyTable = this.binaryLocationsPerProject.keyTable;
        for (int i = 0, l = keyTable.length; i < l; i++) {
            IProject prereqProject = (IProject) keyTable[i];
            if (prereqProject != null && prereqProject != this.currentProject)
                state.recordStructuralDependency(prereqProject, getLastState(prereqProject));
        }

        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        DataOutputStream out = new DataOutputStream(baos);
        try {
            state.write(out);
        } catch (IOException e) {
            e.printStackTrace();
        }

        ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
        DataInputStream in = new DataInputStream(bais);
        org.eclipse.jdt.internal.core.builder.State newState = null;
        try {
            newState = org.eclipse.jdt.internal.core.builder.JavaBuilder.readState(currentProject, in);
        } catch (IOException e) {
            e.printStackTrace();
        }

        if (DEBUG)
            System.out.println("JavaBuilder: Recording new state : " + state); //$NON-NLS-1$
        // state.dump();
        JavaModelManager.getJavaModelManager().setLastBuiltState(currentProject, newState);
    }

    /**
     * String representation for debugging purposes
     */
    public String toString() {
        return this.currentProject == null ? "JavaBuilder for unknown project" //$NON-NLS-1$
                : "JavaBuilder for " + this.currentProject.getName(); //$NON-NLS-1$
    }
}