/******************************************************************************* * Copyright (c) 2000, 2003 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Common Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/cpl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package net.sourceforge.phpdt.internal.core.builder; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.util.ArrayList; import java.util.Date; import java.util.HashSet; import java.util.Iterator; import java.util.Map; import net.sourceforge.phpdt.core.IClasspathEntry; import net.sourceforge.phpdt.core.IJavaModelMarker; import net.sourceforge.phpdt.core.JavaCore; import net.sourceforge.phpdt.core.JavaModelException; import net.sourceforge.phpdt.core.compiler.CharOperation; import net.sourceforge.phpdt.internal.core.JavaModel; import net.sourceforge.phpdt.internal.core.JavaModelManager; import net.sourceforge.phpdt.internal.core.JavaProject; import net.sourceforge.phpdt.internal.core.util.SimpleLookupTable; import net.sourceforge.phpdt.internal.core.util.Util; import net.sourceforge.phpdt.internal.core.util.PHPFileUtil; import net.sourceforge.phpeclipse.PHPeclipsePlugin; import net.sourceforge.phpeclipse.builder.IdentifierIndexManager; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IMarker; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; //import org.eclipse.core.resources.IResourceChangeEvent; import org.eclipse.core.resources.IResourceDelta; import org.eclipse.core.resources.IResourceVisitor; import org.eclipse.core.resources.IWorkspaceRoot; 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.OperationCanceledException; public class PHPBuilder extends IncrementalProjectBuilder { IProject currentProject; JavaProject javaProject; IWorkspaceRoot workspaceRoot; NameEnvironment nameEnvironment; SimpleLookupTable binaryLocationsPerProject; // maps a project to its // binary // resources (output folders, // class folders, zip/jar files) State lastState; BuildNotifier notifier; char[][] extraResourceFileFilters; String[] extraResourceFolderFilters; public static final String CLASS_EXTENSION = "class"; //$NON-NLS-1$ public static boolean DEBUG = 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()) return resource.findMarkers( IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER, false, IResource.DEPTH_INFINITE); } 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]; } // public static void finishedBuilding(IResourceChangeEvent event) { // BuildNotifier.resetProblemCounters(); // } /** * 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() { // do nothing // TODO (philippe) is it still needed? } /** * 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); } 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); } } 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); } public PHPBuilder() { } protected IProject[] build(int kind, Map ignored, IProgressMonitor monitor) throws CoreException { this.currentProject = getProject(); if (currentProject == null || !currentProject.isAccessible()) return new IProject[0]; if (DEBUG) System.out .println("\nStarting build of " + currentProject.getName() //$NON-NLS-1$ + " @ " + new Date(System.currentTimeMillis())); //$NON-NLS-1$ this.notifier = new BuildNotifier(monitor, currentProject); notifier.begin(); boolean ok = false; try { notifier.checkCancel(); initializeBuilder(); if (isWorthBuilding()) { if (kind == FULL_BUILD) { processFullPHPIndex(currentProject, monitor); buildAll(); } else { if ((this.lastState = getLastState(currentProject)) == null) { if (DEBUG) System.out .println("Performing full build since last saved state was not found"); //$NON-NLS-1$ processFullPHPIndex(currentProject, monitor); buildAll(); // } else if (hasClasspathChanged()) { // // if the output location changes, do not delete the // binary files // // from old location // // the user may be trying something // buildAll(); } else if (nameEnvironment.sourceLocations.length > 0) { // if there is no source to compile & no classpath // changes then we // are done SimpleLookupTable deltas = findDeltas(); if (deltas == null) buildAll(); else if (deltas.elementSize > 0) buildDeltas(deltas); else if (DEBUG) System.out .println("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 processFullPHPIndex(currentProject, monitor); buildAll(); } else { if (DEBUG) System.out .println("Nothing to build since there are no source folders and no deltas"); //$NON-NLS-1$ lastState.tagAsNoopBuild(); } } } ok = true; } } catch (CoreException e) { Util.log(e, "JavaBuilder handling CoreException"); //$NON-NLS-1$ IMarker marker = currentProject .createMarker(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER); marker.setAttribute(IMarker.MESSAGE, Util.bind( "build.inconsistentProject", e.getLocalizedMessage())); //$NON-NLS-1$ marker.setAttribute(IMarker.SEVERITY, IMarker.SEVERITY_ERROR); } catch (ImageBuilderInternalException e) { Util.log(e.getThrowable(), "JavaBuilder handling ImageBuilderInternalException"); //$NON-NLS-1$ IMarker marker = currentProject .createMarker(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER); marker .setAttribute( IMarker.MESSAGE, Util .bind( "build.inconsistentProject", e.coreException.getLocalizedMessage())); //$NON-NLS-1$ marker.setAttribute(IMarker.SEVERITY, IMarker.SEVERITY_ERROR); } catch (MissingClassFileException e) { // do not log this exception since its thrown to handle aborted // compiles // because of missing class files if (DEBUG) System.out.println(Util.bind("build.incompleteClassPath", e.missingClassFile)); //$NON-NLS-1$ IMarker marker = currentProject .createMarker(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER); marker.setAttribute(IMarker.MESSAGE, Util.bind( "build.incompleteClassPath", e.missingClassFile)); //$NON-NLS-1$ marker.setAttribute(IMarker.SEVERITY, IMarker.SEVERITY_ERROR); } 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(Util.bind("build.missingSourceFile", e.missingSourceFile)); //$NON-NLS-1$ removeProblemsAndTasksFor(currentProject); // make this the only // problem // for this project IMarker marker = currentProject .createMarker(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER); marker.setAttribute(IMarker.MESSAGE, Util.bind( "build.missingSourceFile", e.missingSourceFile)); //$NON-NLS-1$ marker.setAttribute(IMarker.SEVERITY, IMarker.SEVERITY_ERROR); } catch (Exception e) { e.printStackTrace(); } finally { if (!ok) // If the build failed, clear the previously built state, // forcing a // full build next time. clearLastState(); notifier.done(); cleanup(); } IProject[] requiredProjects = getRequiredProjects(true); if (DEBUG) System.out.println("Finished build of " + currentProject.getName() //$NON-NLS-1$ + " @ " + new Date(System.currentTimeMillis())); //$NON-NLS-1$ return requiredProjects; } /** * Performs a FULL_BUILD by visiting all nodes in the * resource tree under the specified project. * * @param iProject */ private void processFullPHPIndex(final IProject iProject, final IProgressMonitor monitor) { final IdentifierIndexManager indexManager = PHPeclipsePlugin .getDefault().getIndexManager(iProject); // Create resource visitor logic IResourceVisitor myVisitor = new IResourceVisitor() { public boolean visit(IResource resource) throws CoreException { if (resource.getType() == IResource.FILE) { if (monitor.isCanceled()) { throw new OperationCanceledException(); } if ((resource.getFileExtension() != null) && PHPFileUtil.isPHPFile((IFile) resource)) { monitor.worked(1); monitor.subTask("Parsing: " + resource.getFullPath()); // update indexfile for the project: // PHPProject nature = (PHPProject) iProject // .getNature(PHPeclipsePlugin.PHP_NATURE_ID); indexManager.addFile((IFile) resource); } } return true; } }; // Process the project using the visitor just created try { // if (iProject.hasNature(PHPeclipsePlugin.PHP_NATURE_ID)) { // thePHPProject = new PHPProject(); // thePHPProject.setProject(iProject); // } indexManager.initialize(); iProject.accept(myVisitor); indexManager.writeFile(); } catch (CoreException e) { e.printStackTrace(); } } private void buildAll() { notifier.checkCancel(); notifier.subTask(Util.bind("build.preparingBuild")); //$NON-NLS-1$ if (DEBUG && lastState != null) System.out.println("Clearing last state : " + lastState); //$NON-NLS-1$ clearLastState(); BatchImageBuilder imageBuilder = new BatchImageBuilder(this); imageBuilder.build(); recordNewState(imageBuilder.newState); } private void buildDeltas(SimpleLookupTable deltas) { notifier.checkCancel(); notifier.subTask(Util.bind("build.preparingBuild")); //$NON-NLS-1$ if (DEBUG && lastState != null) System.out.println("Clearing last state : " + 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 IncrementalImageBuilder(this); if (imageBuilder.build(deltas)) recordNewState(imageBuilder.newState); else { processFullPHPIndex(currentProject, notifier.monitor); buildAll(); } } private void cleanup() { this.nameEnvironment = null; this.binaryLocationsPerProject = null; this.lastState = null; this.notifier = null; this.extraResourceFileFilters = null; this.extraResourceFolderFilters = null; } private void clearLastState() { JavaModelManager.getJavaModelManager().setLastBuiltState( currentProject, null); } boolean filterExtraResource(IResource resource) { if (extraResourceFileFilters != null) { char[] name = resource.getName().toCharArray(); for (int i = 0, l = extraResourceFileFilters.length; i < l; i++) if (CharOperation .match(extraResourceFileFilters[i], name, true)) return true; } if (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 = extraResourceFolderFilters.length; i < l; i++) if (pathName.indexOf(extraResourceFolderFilters[i]) != -1) for (int j = 0; j < count; j++) if (extraResourceFolderFilters[i].equals(path .segment(j))) return true; } return false; } private SimpleLookupTable findDeltas() { notifier.subTask(Util.bind( "build.readingDelta", currentProject.getName())); //$NON-NLS-1$ IResourceDelta delta = getDelta(currentProject); SimpleLookupTable deltas = new SimpleLookupTable(3); if (delta != null) { if (delta.getKind() != IResourceDelta.NO_CHANGE) { if (DEBUG) System.out.println("Found source delta for: " + currentProject.getName()); //$NON-NLS-1$ deltas.put(currentProject, delta); } } else { if (DEBUG) System.out .println("Missing delta for: " + currentProject.getName()); //$NON-NLS-1$ notifier.subTask(""); //$NON-NLS-1$ return null; } Object[] keyTable = binaryLocationsPerProject.keyTable; //Object[] valueTable = binaryLocationsPerProject.valueTable; nextProject: for (int i = 0, l = keyTable.length; i < l; i++) { IProject p = (IProject) keyTable[i]; if (p != null && p != currentProject) { State s = getLastState(p); if (!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 } notifier.subTask(Util.bind("build.readingDelta", p.getName())); //$NON-NLS-1$ delta = getDelta(p); if (delta != null) { if (delta.getKind() != IResourceDelta.NO_CHANGE) { if (DEBUG) System.out .println("Found binary delta for: " + p.getName()); //$NON-NLS-1$ deltas.put(p, delta); } } else { if (DEBUG) System.out.println("Missing delta for: " + p.getName()); //$NON-NLS-1$ notifier.subTask(""); //$NON-NLS-1$ return null; } } } notifier.subTask(""); //$NON-NLS-1$ return deltas; } private State getLastState(IProject project) { return (State) JavaModelManager.getJavaModelManager() .getLastBuiltState(project, notifier.monitor); } /* * 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 (javaProject == null || workspaceRoot == null) return new IProject[0]; ArrayList projects = new ArrayList(); try { IClasspathEntry[] entries = javaProject.getExpandedClasspath(true); 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 = workspaceRoot.getProject(path.lastSegment()); // missing // projects are // considered too break; case IClasspathEntry.CPE_LIBRARY: if (includeBinaryPrerequisites && path.segmentCount() > 1) { // some binary resources on the class path can come from // projects // that are not included in the project references IResource resource = workspaceRoot.findMember(path .segment(0)); if (resource instanceof IProject) p = (IProject) resource; } } 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; } // private boolean hasClasspathChanged() { // ClasspathMultiDirectory[] newSourceLocations = // nameEnvironment.sourceLocations; // ClasspathMultiDirectory[] oldSourceLocations = 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; // } // } catch (CoreException ignore) { // } // if (DEBUG) // System.out.println(newSourceLocations[n] + " != " // + oldSourceLocations[o]); //$NON-NLS-1$ // return true; // } // while (n < newLength) { // try { // if (newSourceLocations[n].sourceFolder.members().length == 0) { // added // // new // // empty // // source // // folder // n++; // continue; // } // } catch (CoreException ignore) { // } // if (DEBUG) // System.out.println("Added non-empty source folder"); //$NON-NLS-1$ // return true; // } // if (o < oldLength) { // if (DEBUG) // System.out.println("Removed source folder"); //$NON-NLS-1$ // return true; // } // // ClasspathLocation[] newBinaryLocations = // // nameEnvironment.binaryLocations; // // ClasspathLocation[] oldBinaryLocations = 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(newBinaryLocations[n] + " != " + // // oldBinaryLocations[o]); //$NON-NLS-1$ // // return true; // // } // // if (n < newLength || o < oldLength) { // // if (DEBUG) // // System.out.println("Number of binary folders/jar files has changed"); // // //$NON-NLS-1$ // // 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(currentProject); if (delta != null && delta.getKind() != IResourceDelta.NO_CHANGE) { // ClasspathLocation[] classFoldersAndJars = (ClasspathLocation[]) // binaryLocationsPerProject.get(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 void initializeBuilder() throws CoreException { this.javaProject = (JavaProject) JavaCore.create(currentProject); this.workspaceRoot = currentProject.getWorkspace().getRoot(); // Flush the existing external files cache if this is the beginning of a // build cycle String projectName = currentProject.getName(); if (builtProjects == null || builtProjects.contains(projectName)) { JavaModel.flushExternalFileCache(); builtProjects = new ArrayList(); } builtProjects.add(projectName); this.binaryLocationsPerProject = new SimpleLookupTable(3); this.nameEnvironment = new NameEnvironment(workspaceRoot, javaProject, binaryLocationsPerProject); String filterSequence = 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] == '/') extraResourceFolderFilters[--folderCount] = new String( CharOperation.subarray(f, 0, f.length - 1)); else extraResourceFileFilters[--fileCount] = f; } } } private boolean isClasspathBroken(IClasspathEntry[] classpath, IProject p) throws CoreException { if (classpath == JavaProject.INVALID_CLASSPATH) // the .classpath file // could not be read return true; IMarker[] markers = p.findMarkers( IJavaModelMarker.BUILDPATH_PROBLEM_MARKER, false, IResource.DEPTH_ZERO); for (int i = 0, l = markers.length; i < l; i++) if (((Integer) markers[i].getAttribute(IMarker.SEVERITY)) .intValue() == IMarker.SEVERITY_ERROR) return true; return false; } private boolean isWorthBuilding() throws CoreException { boolean abortBuilds = JavaCore.ABORT.equals(javaProject.getOption( JavaCore.CORE_JAVA_BUILD_INVALID_CLASSPATH, true)); if (!abortBuilds) return true; // Abort build only if there are classpath errors // if (isClasspathBroken(javaProject.getRawClasspath(), currentProject)) // { // if (DEBUG) // System.out.println("Aborted build because project has classpath // errors // (incomplete or involved in cycle)"); //$NON-NLS-1$ // // JavaModelManager.getJavaModelManager().deltaProcessor.addForRefresh(javaProject); // // removeProblemsAndTasksFor(currentProject); // remove all compilation // problems // // IMarker marker = // currentProject.createMarker(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER); // marker.setAttribute(IMarker.MESSAGE, // ProjectPrefUtil.bind("build.abortDueToClasspathProblems")); // //$NON-NLS-1$ // marker.setAttribute(IMarker.SEVERITY, IMarker.SEVERITY_ERROR); // return false; // } // 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); next: 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(javaProject.getOption( JavaCore.CORE_CIRCULAR_CLASSPATH, true))) continue; if (DEBUG) System.out.println("Aborted build because prereq project " + p.getName() //$NON-NLS-1$ + " was not built"); //$NON-NLS-1$ removeProblemsAndTasksFor(currentProject); // make this the // only // problem for this project IMarker marker = currentProject .createMarker(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER); marker .setAttribute( IMarker.MESSAGE, isClasspathBroken(prereq.getRawClasspath(), p) ? Util .bind( "build.prereqProjectHasClasspathProblems", p.getName()) //$NON-NLS-1$ : Util .bind( "build.prereqProjectMustBeRebuilt", p.getName())); //$NON-NLS-1$ marker.setAttribute(IMarker.SEVERITY, IMarker.SEVERITY_ERROR); 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() { HashSet cycleParticipants = new HashSet(3); javaProject.updateCycleParticipants(null, new ArrayList(), cycleParticipants, workspaceRoot, new HashSet(3)); IPath currentPath = 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("Requesting another build iteration since cycle participant " + project.getName() //$NON-NLS-1$ + " has not yet seen some structural changes"); //$NON-NLS-1$ needRebuild(); return; } } } } private void recordNewState(State state) { Object[] keyTable = binaryLocationsPerProject.keyTable; for (int i = 0, l = keyTable.length; i < l; i++) { IProject prereqProject = (IProject) keyTable[i]; if (prereqProject != null && prereqProject != currentProject) state.recordStructuralDependency(prereqProject, getLastState(prereqProject)); } if (DEBUG) System.out.println("Recording new state : " + state); //$NON-NLS-1$ // state.dump(); JavaModelManager.getJavaModelManager().setLastBuiltState( currentProject, state); } /** * String representation for debugging purposes */ public String toString() { return currentProject == null ? "JavaBuilder for unknown project" //$NON-NLS-1$ : "JavaBuilder for " + currentProject.getName(); //$NON-NLS-1$ } }