blob: a562966277730fd5a89b1db6311a1903ee7a58c2 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2005 BEA Systems, Inc.
* 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:
* mkaufman@bea.com - initial API and implementation
*
*******************************************************************************/
// TODO: this class gets constructed and called from JDT Core. So it needs to
// be 1.4-compliant, and to contain the "switch" to disable the rest of the code
// if we are running on 1.4 and/or if tools.jar is unavailable. - WHarley 3/05
package org.eclipse.jdt.apt.core.internal;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.jdt.apt.core.internal.APTDispatch.APTResult;
import org.eclipse.jdt.apt.core.internal.generatedfile.GeneratedFileManager;
import org.eclipse.jdt.apt.core.util.AptConfig;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.compiler.CompilationParticipantEvent;
import org.eclipse.jdt.core.compiler.CompilationParticipantResult;
import org.eclipse.jdt.core.compiler.ICompilationParticipant;
import org.eclipse.jdt.core.compiler.IProblem;
import org.eclipse.jdt.core.compiler.PostReconcileCompilationEvent;
import org.eclipse.jdt.core.compiler.PostReconcileCompilationResult;
import org.eclipse.jdt.core.compiler.PreBuildCompilationEvent;
import org.eclipse.jdt.core.compiler.PreBuildCompilationResult;
import com.sun.mirror.apt.AnnotationProcessorFactory;
public class AptCompilationParticipant implements ICompilationParticipant
{
private static AptCompilationParticipant INSTANCE;
public static AptCompilationParticipant getInstance() {
return INSTANCE;
}
/**
* This class is constructed indirectly, by registering an extension to the
* org.eclipse.jdt.core.compilationParticipants extension point.
*/
public AptCompilationParticipant()
{
_factoryLoader = AnnotationProcessorFactoryLoader.getLoader();
INSTANCE = this;
}
public CompilationParticipantResult notify( CompilationParticipantEvent cpe )
{
if (!AptConfig.isEnabled(cpe.getJavaProject()))
return GENERIC_COMPILATION_RESULT;
if ( cpe == null )
return GENERIC_COMPILATION_RESULT;
else if ( cpe.getKind() == ICompilationParticipant.PRE_BUILD_EVENT )
return preBuildNotify( (PreBuildCompilationEvent) cpe );
else if ( cpe.getKind() == ICompilationParticipant.POST_RECONCILE_EVENT )
return postReconcileNotify( (PostReconcileCompilationEvent) cpe );
else if ( cpe.getKind() == ICompilationParticipant.CLEAN_EVENT )
return cleanNotify( cpe );
else
return GENERIC_COMPILATION_RESULT;
}
private CompilationParticipantResult preBuildNotify( PreBuildCompilationEvent pbce )
{
if ( pbce == null )
return EMPTY_PRE_BUILD_COMPILATION_RESULT;
IFile[] buildFiles = pbce.getFiles();
IJavaProject javaProject = pbce.getJavaProject();
if ( buildFiles == null || buildFiles.length == 0 )
return EMPTY_PRE_BUILD_COMPILATION_RESULT;
HashSet<IFile> newFiles = new HashSet<IFile>();
HashSet<IFile> deletedFiles = new HashSet<IFile>();
HashMap<IFile, Set<String>> newDependencies = new HashMap<IFile, Set<String>>();
HashMap<IFile, List<IProblem>> problems = new HashMap<IFile, List<IProblem>>(4);
List<AnnotationProcessorFactory> factories = _factoryLoader.getFactoriesForProject( javaProject );
for ( int i = 0; i < buildFiles.length; i++ )
{
APTResult result = APTDispatch.runAPTDuringBuild(
factories,
buildFiles[i],
javaProject );
newFiles.addAll( result.getNewFiles() );
deletedFiles.addAll( result.getDeletedFiles() );
newDependencies.put( buildFiles[i], result.getNewDependencies() );
mergeMaps(result.getProblems(), problems);
}
// for apt, new files will always trump deleted files
for ( IFile df : deletedFiles )
if ( newFiles.contains( df ) )
deletedFiles.remove( df );
return new PreBuildCompilationResult( newFiles.toArray( new IFile[ newFiles.size() ] ), deletedFiles.toArray( new IFile[ deletedFiles.size() ] ), newDependencies, problems );
}
/**
* Given a Map which maps from a key to a value, where key is an arbitrary
* type, and where value is a Collection, mergeMaps will ensure that for a key
* k with value v in source, all of the elements in the Collection v will be
* moved into the Collection v' corresponding to key k in the destination Map.
*
* @param source - The source map from some key to a Collection.
* @param destination - The destination map from some key to a Collection
*/
private static void mergeMaps( Map source, Map destination ) {
if( source == null || destination == null ) return;
Iterator keys = source.keySet().iterator();
while( keys.hasNext() ) {
Object key = keys.next();
Object val = destination.get( key );
if ( val != null ) {
Collection c = (Collection) val;
c.addAll( (Collection)source.get( key ) );
}
else {
destination.put( key, source.get( key ) );
}
}
}
private CompilationParticipantResult postReconcileNotify( PostReconcileCompilationEvent prce )
{
IProblem[] problems = null;
try
{
org.eclipse.jdt.core.ICompilationUnit cu = prce.getCompilationUnit();
IJavaProject javaProject = prce.getJavaProject();
// these are null sometimes. Not sure why...
if ( cu == null || javaProject == null )
return GENERIC_COMPILATION_RESULT;
List<AnnotationProcessorFactory> factories = _factoryLoader.getFactoriesForProject( javaProject );
APTResult result = APTDispatch.runAPTDuringReconcile( factories, cu, javaProject );
Map<IFile, List<IProblem>> allproblems = result.getProblems();
final List<IProblem> problemList = allproblems.get((IFile)cu.getResource());
if( problemList != null && !problemList.isEmpty())
problems = problemList.toArray(new IProblem[problemList.size()]);
}
catch ( Throwable t )
{
t.printStackTrace();
}
return new PostReconcileCompilationResult(problems);
}
private CompilationParticipantResult cleanNotify( CompilationParticipantEvent cpe )
{
IProject p = cpe.getJavaProject().getProject();
GeneratedFileManager gfm = GeneratedFileManager.getGeneratedFileManager( p );
gfm.projectClean( true );
return GENERIC_COMPILATION_RESULT;
}
public boolean doesParticipateInProject(IJavaProject project) {
List<AnnotationProcessorFactory> factories = _factoryLoader.getFactoriesForProject( project );
if (factories.size() == 0)
return false;
//TODO: use config to decide which projects we support
return true;
}
private AnnotationProcessorFactoryLoader _factoryLoader;
private final static String DOT_JAVA = ".java";
private final static PreBuildCompilationResult EMPTY_PRE_BUILD_COMPILATION_RESULT =
new PreBuildCompilationResult( new IFile[0], new IFile[0], Collections.emptyMap(), Collections.emptyMap() );
private final static CompilationParticipantResult GENERIC_COMPILATION_RESULT =
new CompilationParticipantResult();
}