/******************************************************************************* * Copyright (c) 2000, 2009 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 org.eclipse.jdt.internal.core.builder; import org.eclipse.core.runtime.*; import org.eclipse.core.resources.*; import org.eclipse.jdt.core.*; import org.eclipse.jdt.core.compiler.*; import org.eclipse.jdt.internal.compiler.*; import org.eclipse.jdt.internal.compiler.Compiler; import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; import org.eclipse.jdt.internal.compiler.env.ICompilationUnit; import org.eclipse.jdt.internal.compiler.impl.CompilerOptions; import org.eclipse.jdt.internal.compiler.lookup.TypeConstants; import org.eclipse.jdt.internal.compiler.problem.*; import org.eclipse.jdt.internal.compiler.util.SimpleSet; import org.eclipse.jdt.internal.compiler.util.SuffixConstants; import org.eclipse.jdt.internal.core.JavaModelManager; import org.eclipse.jdt.internal.core.util.Messages; import org.eclipse.jdt.internal.core.util.Util; import java.io.*; import java.util.*; /** * The abstract superclass of Java builders. * Provides the building and compilation mechanism * in common with the batch and incremental builders. */ public abstract class AbstractImageBuilder implements ICompilerRequestor, ICompilationUnitLocator { protected JavaBuilder javaBuilder; protected State newState; // local copies protected NameEnvironment nameEnvironment; protected ClasspathMultiDirectory[] sourceLocations; protected BuildNotifier notifier; protected Compiler compiler; protected WorkQueue workQueue; protected ArrayList problemSourceFiles; protected boolean compiledAllAtOnce; private boolean inCompiler; protected boolean keepStoringProblemMarkers; protected SimpleSet filesWithAnnotations = null; public static int MAX_AT_ONCE = 2000; // best compromise between space used and speed public final static String[] JAVA_PROBLEM_MARKER_ATTRIBUTE_NAMES = { IMarker.MESSAGE, IMarker.SEVERITY, IJavaModelMarker.ID, IMarker.CHAR_START, IMarker.CHAR_END, IMarker.LINE_NUMBER, IJavaModelMarker.ARGUMENTS, IJavaModelMarker.CATEGORY_ID, }; public final static String[] JAVA_TASK_MARKER_ATTRIBUTE_NAMES = { IMarker.MESSAGE, IMarker.PRIORITY, IJavaModelMarker.ID, IMarker.CHAR_START, IMarker.CHAR_END, IMarker.LINE_NUMBER, IMarker.USER_EDITABLE, IMarker.SOURCE_ID, }; public final static Integer S_ERROR = new Integer(IMarker.SEVERITY_ERROR); public final static Integer S_WARNING = new Integer(IMarker.SEVERITY_WARNING); public final static Integer P_HIGH = new Integer(IMarker.PRIORITY_HIGH); public final static Integer P_NORMAL = new Integer(IMarker.PRIORITY_NORMAL); public final static Integer P_LOW = new Integer(IMarker.PRIORITY_LOW); protected AbstractImageBuilder(JavaBuilder javaBuilder, boolean buildStarting, State newState) { // local copies this.javaBuilder = javaBuilder; this.nameEnvironment = javaBuilder.nameEnvironment; this.sourceLocations = this.nameEnvironment.sourceLocations; this.notifier = javaBuilder.notifier; this.keepStoringProblemMarkers = true; // may get disabled when missing classfiles are encountered if (buildStarting) { this.newState = newState == null ? new State(javaBuilder) : newState; this.compiler = newCompiler(); this.workQueue = new WorkQueue(); this.problemSourceFiles = new ArrayList(3); if (this.javaBuilder.participants != null) { for (int i = 0, l = this.javaBuilder.participants.length; i < l; i++) { if (this.javaBuilder.participants[i].isAnnotationProcessor()) { // initialize this set so the builder knows to gather CUs that define Annotation types // each Annotation processor participant is then asked to process these files AFTER // the compile loop. The normal dependency loop will then recompile all affected types this.filesWithAnnotations = new SimpleSet(1); break; } } } } } public void acceptResult(CompilationResult result) { // In Batch mode, we write out the class files, hold onto the dependency info // & additional types and report problems. // In Incremental mode, when writing out a class file we need to compare it // against the previous file, remembering if structural changes occured. // Before reporting the new problems, we need to update the problem count & // remove the old problems. Plus delete additional class files that no longer exist. SourceFile compilationUnit = (SourceFile) result.getCompilationUnit(); // go directly back to the sourceFile if (!this.workQueue.isCompiled(compilationUnit)) { this.workQueue.finished(compilationUnit); try { updateProblemsFor(compilationUnit, result); // record compilation problems before potentially adding duplicate errors updateTasksFor(compilationUnit, result); // record tasks } catch (CoreException e) { throw internalException(e); } if (result.hasInconsistentToplevelHierarchies) // ensure that this file is always retrieved from source for the rest of the build if (!this.problemSourceFiles.contains(compilationUnit)) this.problemSourceFiles.add(compilationUnit); IType mainType = null; String mainTypeName = null; String typeLocator = compilationUnit.typeLocator(); ClassFile[] classFiles = result.getClassFiles(); int length = classFiles.length; ArrayList duplicateTypeNames = null; ArrayList definedTypeNames = new ArrayList(length); for (int i = 0; i < length; i++) { ClassFile classFile = classFiles[i]; char[][] compoundName = classFile.getCompoundName(); char[] typeName = compoundName[compoundName.length - 1]; boolean isNestedType = classFile.isNestedType; // Look for a possible collision, if one exists, report an error but do not write the class file if (isNestedType) { String qualifiedTypeName = new String(classFile.outerMostEnclosingClassFile().fileName()); if (this.newState.isDuplicateLocator(qualifiedTypeName, typeLocator)) continue; } else { String qualifiedTypeName = new String(classFile.fileName()); // the qualified type name "p1/p2/A" if (this.newState.isDuplicateLocator(qualifiedTypeName, typeLocator)) { if (duplicateTypeNames == null) duplicateTypeNames = new ArrayList(); duplicateTypeNames.add(compoundName); if (mainType == null) { try { mainTypeName = compilationUnit.initialTypeName; // slash separated qualified name "p1/p1/A" mainType = this.javaBuilder.javaProject.findType(mainTypeName.replace('/', '.')); } catch (JavaModelException e) { // ignore } } IType type; if (qualifiedTypeName.equals(mainTypeName)) { type = mainType; } else { String simpleName = qualifiedTypeName.substring(qualifiedTypeName.lastIndexOf('/')+1); type = mainType == null ? null : mainType.getCompilationUnit().getType(simpleName); } createProblemFor(compilationUnit.resource, type, Messages.bind(Messages.build_duplicateClassFile, new String(typeName)), JavaCore.ERROR); continue; } this.newState.recordLocatorForType(qualifiedTypeName, typeLocator); if (result.checkSecondaryTypes && !qualifiedTypeName.equals(compilationUnit.initialTypeName)) acceptSecondaryType(classFile); } try { definedTypeNames.add(writeClassFile(classFile, compilationUnit, !isNestedType)); } catch (CoreException e) { Util.log(e, "JavaBuilder handling CoreException"); //$NON-NLS-1$ if (e.getStatus().getCode() == IResourceStatus.CASE_VARIANT_EXISTS) createProblemFor(compilationUnit.resource, null, Messages.bind(Messages.build_classFileCollision, e.getMessage()), JavaCore.ERROR); else createProblemFor(compilationUnit.resource, null, Messages.build_inconsistentClassFile, JavaCore.ERROR); } } if (result.hasAnnotations && this.filesWithAnnotations != null) // only initialized if an annotation processor is attached this.filesWithAnnotations.add(compilationUnit); this.compiler.lookupEnvironment.releaseClassFiles(classFiles); finishedWith(typeLocator, result, compilationUnit.getMainTypeName(), definedTypeNames, duplicateTypeNames); this.notifier.compiled(compilationUnit); } } protected void acceptSecondaryType(ClassFile classFile) { // noop } protected void addAllSourceFiles(final ArrayList sourceFiles) throws CoreException { for (int i = 0, l = this.sourceLocations.length; i < l; i++) { final ClasspathMultiDirectory sourceLocation = this.sourceLocations[i]; final char[][] exclusionPatterns = sourceLocation.exclusionPatterns; final char[][] inclusionPatterns = sourceLocation.inclusionPatterns; final boolean isAlsoProject = sourceLocation.sourceFolder.equals(this.javaBuilder.currentProject); final int segmentCount = sourceLocation.sourceFolder.getFullPath().segmentCount(); final IContainer outputFolder = sourceLocation.binaryFolder; final boolean isOutputFolder = sourceLocation.sourceFolder.equals(outputFolder); sourceLocation.sourceFolder.accept( new IResourceProxyVisitor() { public boolean visit(IResourceProxy proxy) throws CoreException { switch(proxy.getType()) { case IResource.FILE : if (org.eclipse.jdt.internal.core.util.Util.isJavaLikeFileName(proxy.getName())) { IResource resource = proxy.requestResource(); if (exclusionPatterns != null || inclusionPatterns != null) if (Util.isExcluded(resource.getFullPath(), inclusionPatterns, exclusionPatterns, false)) return false; sourceFiles.add(new SourceFile((IFile) resource, sourceLocation)); } return false; case IResource.FOLDER : IPath folderPath = null; if (isAlsoProject) if (isExcludedFromProject(folderPath = proxy.requestFullPath())) return false; if (exclusionPatterns != null) { if (folderPath == null) folderPath = proxy.requestFullPath(); if (Util.isExcluded(folderPath, inclusionPatterns, exclusionPatterns, true)) { // must walk children if inclusionPatterns != null, can skip them if == null // but folder is excluded so do not create it in the output folder return inclusionPatterns != null; } } if (!isOutputFolder) { if (folderPath == null) folderPath = proxy.requestFullPath(); String packageName = folderPath.lastSegment(); if (packageName.length() > 0) { String sourceLevel = AbstractImageBuilder.this.javaBuilder.javaProject.getOption(JavaCore.COMPILER_SOURCE, true); String complianceLevel = AbstractImageBuilder.this.javaBuilder.javaProject.getOption(JavaCore.COMPILER_COMPLIANCE, true); if (JavaConventions.validatePackageName(packageName, sourceLevel, complianceLevel).getSeverity() != IStatus.ERROR) createFolder(folderPath.removeFirstSegments(segmentCount), outputFolder); } } } return true; } }, IResource.NONE ); this.notifier.checkCancel(); } } protected void cleanUp() { this.nameEnvironment.cleanup(); this.javaBuilder = null; this.nameEnvironment = null; this.sourceLocations = null; this.notifier = null; this.compiler = null; this.workQueue = null; this.problemSourceFiles = null; } /* Compile the given elements, adding more elements to the work queue * if they are affected by the changes. */ protected void compile(SourceFile[] units) { if (this.filesWithAnnotations != null && this.filesWithAnnotations.elementSize > 0) // will add files that have annotations in acceptResult() & then processAnnotations() before exitting this method this.filesWithAnnotations.clear(); // notify CompilationParticipants which source files are about to be compiled CompilationParticipantResult[] participantResults = this.javaBuilder.participants == null ? null : notifyParticipants(units); if (participantResults != null && participantResults.length > units.length) { units = new SourceFile[participantResults.length]; for (int i = participantResults.length; --i >= 0;) units[i] = participantResults[i].sourceFile; } int unitsLength = units.length; this.compiledAllAtOnce = unitsLength <= MAX_AT_ONCE; if (this.compiledAllAtOnce) { // do them all now if (JavaBuilder.DEBUG) for (int i = 0; i < unitsLength; i++) System.out.println("About to compile " + units[i].typeLocator()); //$NON-NLS-1$ compile(units, null, true); } else { SourceFile[] remainingUnits = new SourceFile[unitsLength]; // copy of units, removing units when about to compile System.arraycopy(units, 0, remainingUnits, 0, unitsLength); int doNow = unitsLength < MAX_AT_ONCE ? unitsLength : MAX_AT_ONCE; SourceFile[] toCompile = new SourceFile[doNow]; int remainingIndex = 0; boolean compilingFirstGroup = true; while (remainingIndex < unitsLength) { int count = 0; while (remainingIndex < unitsLength && count < doNow) { // Although it needed compiling when this method was called, it may have // already been compiled when it was referenced by another unit. SourceFile unit = remainingUnits[remainingIndex]; if (unit != null && (compilingFirstGroup || this.workQueue.isWaiting(unit))) { if (JavaBuilder.DEBUG) System.out.println("About to compile #" + remainingIndex + " : "+ unit.typeLocator()); //$NON-NLS-1$ //$NON-NLS-2$ toCompile[count++] = unit; } remainingUnits[remainingIndex++] = null; } if (count < doNow) System.arraycopy(toCompile, 0, toCompile = new SourceFile[count], 0, count); if (!compilingFirstGroup) for (int a = remainingIndex; a < unitsLength; a++) if (remainingUnits[a] != null && this.workQueue.isCompiled(remainingUnits[a])) remainingUnits[a] = null; // use the class file for this source file since its been compiled compile(toCompile, remainingUnits, compilingFirstGroup); compilingFirstGroup = false; } } if (participantResults != null) { for (int i = participantResults.length; --i >= 0;) if (participantResults[i] != null) recordParticipantResult(participantResults[i]); processAnnotations(participantResults); } } protected void compile(SourceFile[] units, SourceFile[] additionalUnits, boolean compilingFirstGroup) { if (units.length == 0) return; this.notifier.aboutToCompile(units[0]); // just to change the message // extend additionalFilenames with all hierarchical problem types found during this entire build if (!this.problemSourceFiles.isEmpty()) { int toAdd = this.problemSourceFiles.size(); int length = additionalUnits == null ? 0 : additionalUnits.length; if (length == 0) additionalUnits = new SourceFile[toAdd]; else System.arraycopy(additionalUnits, 0, additionalUnits = new SourceFile[length + toAdd], 0, length); for (int i = 0; i < toAdd; i++) additionalUnits[length + i] = (SourceFile) this.problemSourceFiles.get(i); } String[] initialTypeNames = new String[units.length]; for (int i = 0, l = units.length; i < l; i++) initialTypeNames[i] = units[i].initialTypeName; this.nameEnvironment.setNames(initialTypeNames, additionalUnits); this.notifier.checkCancel(); try { this.inCompiler = true; this.compiler.compile(units); } catch (AbortCompilation ignored) { // ignore the AbortCompilcation coming from BuildNotifier.checkCancelWithinCompiler() // the Compiler failed after the user has chose to cancel... likely due to an OutOfMemory error } finally { this.inCompiler = false; } // Check for cancel immediately after a compile, because the compiler may // have been cancelled but without propagating the correct exception this.notifier.checkCancel(); } protected void copyResource(IResource source, IResource destination) throws CoreException { IPath destPath = destination.getFullPath(); try { source.copy(destPath, IResource.FORCE | IResource.DERIVED, null); } catch (CoreException e) { // handle the case when the source resource is deleted source.refreshLocal(0, null); if (!source.exists()) return; // source resource was deleted so skip it throw e; } Util.setReadOnly(destination, false); // just in case the original was read only } protected void createProblemFor(IResource resource, IMember javaElement, String message, String problemSeverity) { try { IMarker marker = resource.createMarker(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER); int severity = problemSeverity.equals(JavaCore.WARNING) ? IMarker.SEVERITY_WARNING : IMarker.SEVERITY_ERROR; ISourceRange range = null; if (javaElement != null) { try { range = javaElement.getNameRange(); } catch (JavaModelException e) { if (e.getJavaModelStatus().getCode() != IJavaModelStatusConstants.ELEMENT_DOES_NOT_EXIST) { throw e; } if (!CharOperation.equals(javaElement.getElementName().toCharArray(), TypeConstants.PACKAGE_INFO_NAME)) { throw e; } // else silently swallow the exception as the synthetic interface type package-info has no // source range really. See https://bugs.eclipse.org/bugs/show_bug.cgi?id=258145 } } int start = range == null ? 0 : range.getOffset(); int end = range == null ? 1 : start + range.getLength(); marker.setAttributes( new String[] {IMarker.MESSAGE, IMarker.SEVERITY, IMarker.CHAR_START, IMarker.CHAR_END, IMarker.SOURCE_ID}, new Object[] {message, new Integer(severity), new Integer(start), new Integer(end), JavaBuilder.SOURCE_ID}); } catch (CoreException e) { throw internalException(e); } } protected void deleteGeneratedFiles(IFile[] deletedGeneratedFiles) { // no op by default } protected SourceFile findSourceFile(IFile file, boolean mustExist) { if (mustExist && !file.exists()) return null; // assumes the file exists in at least one of the source folders & is not excluded ClasspathMultiDirectory md = this.sourceLocations[0]; if (this.sourceLocations.length > 1) { IPath sourceFileFullPath = file.getFullPath(); for (int j = 0, m = this.sourceLocations.length; j < m; j++) { if (this.sourceLocations[j].sourceFolder.getFullPath().isPrefixOf(sourceFileFullPath)) { md = this.sourceLocations[j]; if (md.exclusionPatterns == null && md.inclusionPatterns == null) break; if (!Util.isExcluded(file, md.inclusionPatterns, md.exclusionPatterns)) break; } } } return new SourceFile(file, md); } protected void finishedWith(String sourceLocator, CompilationResult result, char[] mainTypeName, ArrayList definedTypeNames, ArrayList duplicateTypeNames) { if (duplicateTypeNames == null) { this.newState.record(sourceLocator, result.qualifiedReferences, result.simpleNameReferences, result.rootReferences, mainTypeName, definedTypeNames); return; } char[][] simpleRefs = result.simpleNameReferences; // for each duplicate type p1.p2.A, add the type name A (package was already added) next : for (int i = 0, l = duplicateTypeNames.size(); i < l; i++) { char[][] compoundName = (char[][]) duplicateTypeNames.get(i); char[] typeName = compoundName[compoundName.length - 1]; int sLength = simpleRefs.length; for (int j = 0; j < sLength; j++) if (CharOperation.equals(simpleRefs[j], typeName)) continue next; System.arraycopy(simpleRefs, 0, simpleRefs = new char[sLength + 1][], 0, sLength); simpleRefs[sLength] = typeName; } this.newState.record(sourceLocator, result.qualifiedReferences, simpleRefs, result.rootReferences, mainTypeName, definedTypeNames); } protected IContainer createFolder(IPath packagePath, IContainer outputFolder) throws CoreException { if (packagePath.isEmpty()) return outputFolder; IFolder folder = outputFolder.getFolder(packagePath); if (!folder.exists()) { createFolder(packagePath.removeLastSegments(1), outputFolder); folder.create(IResource.FORCE | IResource.DERIVED, true, null); } return folder; } /* (non-Javadoc) * @see org.eclipse.jdt.internal.core.builder.ICompilationUnitLocator#fromIFile(org.eclipse.core.resources.IFile) */ public ICompilationUnit fromIFile(IFile file) { return findSourceFile(file, true); } protected void initializeAnnotationProcessorManager(Compiler newCompiler) { AbstractAnnotationProcessorManager annotationManager = JavaModelManager.getJavaModelManager().createAnnotationProcessorManager(); if (annotationManager != null) { annotationManager.configureFromPlatform(newCompiler, this, this.javaBuilder.javaProject); annotationManager.setErr(new PrintWriter(System.err)); annotationManager.setOut(new PrintWriter(System.out)); } newCompiler.annotationProcessorManager = annotationManager; } protected RuntimeException internalException(CoreException t) { ImageBuilderInternalException imageBuilderException = new ImageBuilderInternalException(t); if (this.inCompiler) return new AbortCompilation(true, imageBuilderException); return imageBuilderException; } protected boolean isExcludedFromProject(IPath childPath) throws JavaModelException { // answer whether the folder should be ignored when walking the project as a source folder if (childPath.segmentCount() > 2) return false; // is a subfolder of a package for (int j = 0, k = this.sourceLocations.length; j < k; j++) { if (childPath.equals(this.sourceLocations[j].binaryFolder.getFullPath())) return true; if (childPath.equals(this.sourceLocations[j].sourceFolder.getFullPath())) return true; } // skip default output folder which may not be used by any source folder return childPath.equals(this.javaBuilder.javaProject.getOutputLocation()); } protected Compiler newCompiler() { // disable entire javadoc support if not interested in diagnostics Map projectOptions = this.javaBuilder.javaProject.getOptions(true); String option = (String) projectOptions.get(JavaCore.COMPILER_PB_INVALID_JAVADOC); if (option == null || option.equals(JavaCore.IGNORE)) { // TODO (frederic) see why option is null sometimes while running model tests!? option = (String) projectOptions.get(JavaCore.COMPILER_PB_MISSING_JAVADOC_TAGS); if (option == null || option.equals(JavaCore.IGNORE)) { option = (String) projectOptions.get(JavaCore.COMPILER_PB_MISSING_JAVADOC_COMMENTS); if (option == null || option.equals(JavaCore.IGNORE)) { option = (String) projectOptions.get(JavaCore.COMPILER_PB_UNUSED_IMPORT); if (option == null || option.equals(JavaCore.IGNORE)) { // Unused import need also to look inside javadoc comment projectOptions.put(JavaCore.COMPILER_DOC_COMMENT_SUPPORT, JavaCore.DISABLED); } } } } // called once when the builder is initialized... can override if needed CompilerOptions compilerOptions = new CompilerOptions(projectOptions); compilerOptions.performMethodsFullRecovery = true; compilerOptions.performStatementsRecovery = true; Compiler newCompiler = new Compiler( this.nameEnvironment, DefaultErrorHandlingPolicies.proceedWithAllProblems(), compilerOptions, this, ProblemFactory.getProblemFactory(Locale.getDefault())); CompilerOptions options = newCompiler.options; // temporary code to allow the compiler to revert to a single thread String setting = System.getProperty("jdt.compiler.useSingleThread"); //$NON-NLS-1$ newCompiler.useSingleThread = setting != null && setting.equals("true"); //$NON-NLS-1$ // enable the compiler reference info support options.produceReferenceInfo = true; if (options.complianceLevel >= ClassFileConstants.JDK1_6 && options.processAnnotations) { // support for Java 6 annotation processors initializeAnnotationProcessorManager(newCompiler); } return newCompiler; } protected CompilationParticipantResult[] notifyParticipants(SourceFile[] unitsAboutToCompile) { CompilationParticipantResult[] results = new CompilationParticipantResult[unitsAboutToCompile.length]; for (int i = unitsAboutToCompile.length; --i >= 0;) results[i] = new CompilationParticipantResult(unitsAboutToCompile[i]); // TODO (kent) do we expect to have more than one participant? // and if so should we pass the generated files from the each processor to the others to process? // and what happens if some participants do not expect to be called with only a few files, after seeing 'all' the files? for (int i = 0, l = this.javaBuilder.participants.length; i < l; i++) this.javaBuilder.participants[i].buildStarting(results, this instanceof BatchImageBuilder); SimpleSet uniqueFiles = null; CompilationParticipantResult[] toAdd = null; int added = 0; for (int i = results.length; --i >= 0;) { CompilationParticipantResult result = results[i]; if (result == null) continue; IFile[] deletedGeneratedFiles = result.deletedFiles; if (deletedGeneratedFiles != null) deleteGeneratedFiles(deletedGeneratedFiles); IFile[] addedGeneratedFiles = result.addedFiles; if (addedGeneratedFiles != null) { for (int j = addedGeneratedFiles.length; --j >= 0;) { SourceFile sourceFile = findSourceFile(addedGeneratedFiles[j], true); if (sourceFile == null) continue; if (uniqueFiles == null) { uniqueFiles = new SimpleSet(unitsAboutToCompile.length + 3); for (int f = unitsAboutToCompile.length; --f >= 0;) uniqueFiles.add(unitsAboutToCompile[f]); } if (uniqueFiles.addIfNotIncluded(sourceFile) == sourceFile) { CompilationParticipantResult newResult = new CompilationParticipantResult(sourceFile); // is there enough room to add all the addedGeneratedFiles.length ? if (toAdd == null) { toAdd = new CompilationParticipantResult[addedGeneratedFiles.length]; } else { int length = toAdd.length; if (added == length) System.arraycopy(toAdd, 0, toAdd = new CompilationParticipantResult[length + addedGeneratedFiles.length], 0, length); } toAdd[added++] = newResult; } } } } if (added >0 ) { int length = results.length; System.arraycopy(results, 0, results = new CompilationParticipantResult[length + added], 0 , length); System.arraycopy(toAdd, 0, results, length, added); } return results; } protected abstract void processAnnotationResults(CompilationParticipantResult[] results); protected void processAnnotations(CompilationParticipantResult[] results) { boolean hasAnnotationProcessor = false; for (int i = 0, l = this.javaBuilder.participants.length; !hasAnnotationProcessor && i < l; i++) hasAnnotationProcessor = this.javaBuilder.participants[i].isAnnotationProcessor(); if (!hasAnnotationProcessor) return; boolean foundAnnotations = this.filesWithAnnotations != null && this.filesWithAnnotations.elementSize > 0; for (int i = results.length; --i >= 0;) results[i].reset(foundAnnotations && this.filesWithAnnotations.includes(results[i].sourceFile)); // even if no files have annotations, must still tell every annotation processor in case the file used to have them for (int i = 0, l = this.javaBuilder.participants.length; i < l; i++) if (this.javaBuilder.participants[i].isAnnotationProcessor()) this.javaBuilder.participants[i].processAnnotations(results); processAnnotationResults(results); } protected void recordParticipantResult(CompilationParticipantResult result) { // any added/changed/deleted generated files have already been taken care // just record the problems and dependencies - do not expect there to be many // must be called after we're finished with the compilation unit results but before incremental loop adds affected files CategorizedProblem[] problems = result.problems; if (problems != null && problems.length > 0) { // existing problems have already been removed so just add these as new problems this.notifier.updateProblemCounts(problems); try { storeProblemsFor(result.sourceFile, problems); } catch (CoreException e) { // must continue with compile loop so just log the CoreException Util.log(e, "JavaBuilder logging CompilationParticipant's CoreException to help debugging"); //$NON-NLS-1$ } } String[] dependencies = result.dependencies; if (dependencies != null) { ReferenceCollection refs = (ReferenceCollection) this.newState.references.get(result.sourceFile.typeLocator()); if (refs != null) refs.addDependencies(dependencies); } } /** * Creates a marker from each problem and adds it to the resource. * The marker is as follows: * - its type is T_PROBLEM * - its plugin ID is the JavaBuilder's plugin ID * - its message is the problem's message * - its priority reflects the severity of the problem * - its range is the problem's range * - it has an extra attribute "ID" which holds the problem's id * - it's {@link IMarker#SOURCE_ID} attribute is positioned to {@link JavaBuilder#SOURCE_ID} if * the problem was generated by JDT; else the {@link IMarker#SOURCE_ID} attribute is * carried from the problem to the marker in extra attributes, if present. */ protected void storeProblemsFor(SourceFile sourceFile, CategorizedProblem[] problems) throws CoreException { if (sourceFile == null || problems == null || problems.length == 0) return; // once a classpath error is found, ignore all other problems for this project so the user can see the main error // but still try to compile as many source files as possible to help the case when the base libraries are in source if (!this.keepStoringProblemMarkers) return; // only want the one error recorded on this source file IResource resource = sourceFile.resource; HashSet managedMarkerTypes = JavaModelManager.getJavaModelManager().compilationParticipants.managedMarkerTypes(); for (int i = 0, l = problems.length; i < l; i++) { CategorizedProblem problem = problems[i]; int id = problem.getID(); // handle missing classfile situation if (id == IProblem.IsClassPathCorrect) { String missingClassfileName = problem.getArguments()[0]; if (JavaBuilder.DEBUG) System.out.println(Messages.bind(Messages.build_incompleteClassPath, missingClassfileName)); boolean isInvalidClasspathError = JavaCore.ERROR.equals(this.javaBuilder.javaProject.getOption(JavaCore.CORE_INCOMPLETE_CLASSPATH, true)); // insert extra classpath problem, and make it the only problem for this project (optional) if (isInvalidClasspathError && JavaCore.ABORT.equals(this.javaBuilder.javaProject.getOption(JavaCore.CORE_JAVA_BUILD_INVALID_CLASSPATH, true))) { JavaBuilder.removeProblemsAndTasksFor(this.javaBuilder.currentProject); // make this the only problem for this project this.keepStoringProblemMarkers = false; } IMarker marker = this.javaBuilder.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_incompleteClassPath, missingClassfileName), new Integer(isInvalidClasspathError ? IMarker.SEVERITY_ERROR : IMarker.SEVERITY_WARNING), new Integer(CategorizedProblem.CAT_BUILDPATH), JavaBuilder.SOURCE_ID } ); // even if we're not keeping more markers, still fall through rest of the problem reporting, so that offending // IsClassPathCorrect problem gets recorded since it may help locate the offending reference } String markerType = problem.getMarkerType(); boolean managedProblem = false; if (IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER.equals(markerType) || (managedProblem = managedMarkerTypes.contains(markerType))) { IMarker marker = resource.createMarker(markerType); String[] attributeNames = JAVA_PROBLEM_MARKER_ATTRIBUTE_NAMES; int standardLength = attributeNames.length; String[] allNames = attributeNames; int managedLength = managedProblem ? 0 : 1; String[] extraAttributeNames = problem.getExtraMarkerAttributeNames(); int extraLength = extraAttributeNames == null ? 0 : extraAttributeNames.length; if (managedLength > 0 || extraLength > 0) { allNames = new String[standardLength + managedLength + extraLength]; System.arraycopy(attributeNames, 0, allNames, 0, standardLength); if (managedLength > 0) allNames[standardLength] = IMarker.SOURCE_ID; System.arraycopy(extraAttributeNames, 0, allNames, standardLength + managedLength, extraLength); } Object[] allValues = new Object[allNames.length]; // standard attributes int index = 0; allValues[index++] = problem.getMessage(); // message allValues[index++] = problem.isError() ? S_ERROR : S_WARNING; // severity allValues[index++] = new Integer(id); // ID allValues[index++] = new Integer(problem.getSourceStart()); // start int end = problem.getSourceEnd(); allValues[index++] = new Integer(end > 0 ? end + 1 : end); // end allValues[index++] = new Integer(problem.getSourceLineNumber()); // line allValues[index++] = Util.getProblemArgumentsForMarker(problem.getArguments()); // arguments allValues[index++] = new Integer(problem.getCategoryID()); // category ID // SOURCE_ID attribute for JDT problems if (managedLength > 0) allValues[index++] = JavaBuilder.SOURCE_ID; // optional extra attributes if (extraLength > 0) System.arraycopy(problem.getExtraMarkerAttributeValues(), 0, allValues, index, extraLength); marker.setAttributes(allNames, allValues); if (!this.keepStoringProblemMarkers) return; // only want the one error recorded on this source file } } } protected void storeTasksFor(SourceFile sourceFile, CategorizedProblem[] tasks) throws CoreException { if (sourceFile == null || tasks == null || tasks.length == 0) return; IResource resource = sourceFile.resource; for (int i = 0, l = tasks.length; i < l; i++) { CategorizedProblem task = tasks[i]; if (task.getID() == IProblem.Task) { IMarker marker = resource.createMarker(IJavaModelMarker.TASK_MARKER); Integer priority = P_NORMAL; String compilerPriority = task.getArguments()[2]; if (JavaCore.COMPILER_TASK_PRIORITY_HIGH.equals(compilerPriority)) priority = P_HIGH; else if (JavaCore.COMPILER_TASK_PRIORITY_LOW.equals(compilerPriority)) priority = P_LOW; String[] attributeNames = JAVA_TASK_MARKER_ATTRIBUTE_NAMES; int standardLength = attributeNames.length; String[] allNames = attributeNames; String[] extraAttributeNames = task.getExtraMarkerAttributeNames(); int extraLength = extraAttributeNames == null ? 0 : extraAttributeNames.length; if (extraLength > 0) { allNames = new String[standardLength + extraLength]; System.arraycopy(attributeNames, 0, allNames, 0, standardLength); System.arraycopy(extraAttributeNames, 0, allNames, standardLength, extraLength); } Object[] allValues = new Object[allNames.length]; // standard attributes int index = 0; allValues[index++] = task.getMessage(); allValues[index++] = priority; allValues[index++] = new Integer(task.getID()); allValues[index++] = new Integer(task.getSourceStart()); allValues[index++] = new Integer(task.getSourceEnd() + 1); allValues[index++] = new Integer(task.getSourceLineNumber()); allValues[index++] = Boolean.FALSE; allValues[index++] = JavaBuilder.SOURCE_ID; // optional extra attributes if (extraLength > 0) System.arraycopy(task.getExtraMarkerAttributeValues(), 0, allValues, index, extraLength); marker.setAttributes(allNames, allValues); } } } protected void updateProblemsFor(SourceFile sourceFile, CompilationResult result) throws CoreException { CategorizedProblem[] problems = result.getProblems(); if (problems == null || problems.length == 0) return; this.notifier.updateProblemCounts(problems); storeProblemsFor(sourceFile, problems); } protected void updateTasksFor(SourceFile sourceFile, CompilationResult result) throws CoreException { CategorizedProblem[] tasks = result.getTasks(); if (tasks == null || tasks.length == 0) return; storeTasksFor(sourceFile, tasks); } protected char[] writeClassFile(ClassFile classFile, SourceFile compilationUnit, boolean isTopLevelType) throws CoreException { String fileName = new String(classFile.fileName()); // the qualified type name "p1/p2/A" IPath filePath = new Path(fileName); IContainer outputFolder = compilationUnit.sourceLocation.binaryFolder; IContainer container = outputFolder; if (filePath.segmentCount() > 1) { container = createFolder(filePath.removeLastSegments(1), outputFolder); filePath = new Path(filePath.lastSegment()); } IFile file = container.getFile(filePath.addFileExtension(SuffixConstants.EXTENSION_class)); writeClassFileContents(classFile, file, fileName, isTopLevelType, compilationUnit); // answer the name of the class file as in Y or Y$M return filePath.lastSegment().toCharArray(); } protected void writeClassFileContents(ClassFile classFile, IFile file, String qualifiedFileName, boolean isTopLevelType, SourceFile compilationUnit) throws CoreException { // InputStream input = new SequenceInputStream( // new ByteArrayInputStream(classFile.header, 0, classFile.headerOffset), // new ByteArrayInputStream(classFile.contents, 0, classFile.contentsOffset)); InputStream input = new ByteArrayInputStream(classFile.getBytes()); if (file.exists()) { // Deal with shared output folders... last one wins... no collision cases detected if (JavaBuilder.DEBUG) System.out.println("Writing changed class file " + file.getName());//$NON-NLS-1$ if (!file.isDerived()) file.setDerived(true, null); file.setContents(input, true, false, null); } else { // Default implementation just writes out the bytes for the new class file... if (JavaBuilder.DEBUG) System.out.println("Writing new class file " + file.getName());//$NON-NLS-1$ file.create(input, IResource.FORCE | IResource.DERIVED, null); } } }