/******************************************************************************* * 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 java.io.ByteArrayInputStream; import java.io.InputStream; import java.io.PrintWriter; import java.util.ArrayList; import java.util.HashSet; import java.util.Locale; import java.util.Map; import org.eclipse.core.resources.IContainer; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IFolder; import org.eclipse.core.resources.IMarker; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IResourceProxy; import org.eclipse.core.resources.IResourceProxyVisitor; import org.eclipse.core.resources.IResourceStatus; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Path; import org.eclipse.jdt.core.IJavaModelMarker; import org.eclipse.jdt.core.IJavaModelStatusConstants; import org.eclipse.jdt.core.IMember; import org.eclipse.jdt.core.ISourceRange; import org.eclipse.jdt.core.IType; import org.eclipse.jdt.core.JavaConventions; import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.core.compiler.CategorizedProblem; import org.eclipse.jdt.core.compiler.CharOperation; import org.eclipse.jdt.core.compiler.IProblem; import org.eclipse.jdt.internal.compiler.AbstractAnnotationProcessorManager; import org.eclipse.jdt.internal.compiler.ClassFile; import org.eclipse.jdt.internal.compiler.CompilationResult; import org.eclipse.jdt.internal.compiler.Compiler; import org.eclipse.jdt.internal.compiler.DefaultErrorHandlingPolicies; import org.eclipse.jdt.internal.compiler.ICompilerRequestor; 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.AbortCompilation; 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; /** * 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); } } }