blob: 472f47ad9e68551e1e9a9efd180c79cfa974f877 [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:
* tyeung@bea.com - initial API and implementation
* mkaufman@bea.com
*******************************************************************************/
package org.eclipse.jdt.apt.core.internal;
import java.util.ArrayList;
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.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jdt.apt.core.internal.declaration.TypeDeclarationImpl;
import org.eclipse.jdt.apt.core.internal.env.EclipseRoundCompleteEvent;
import org.eclipse.jdt.apt.core.internal.env.ProcessorEnvImpl;
import org.eclipse.jdt.apt.core.internal.env.ProcessorEnvImpl.AnnotationVisitor;
import org.eclipse.jdt.apt.core.internal.util.Factory;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.dom.Annotation;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.ITypeBinding;
import com.sun.mirror.apt.AnnotationProcessor;
import com.sun.mirror.apt.AnnotationProcessorFactory;
import com.sun.mirror.apt.AnnotationProcessorListener;
import com.sun.mirror.apt.RoundCompleteListener;
import com.sun.mirror.declaration.AnnotationTypeDeclaration;
/**
* Dispatch APT.
* @author tyeung
*
*/
public class APTDispatch {
public static Set<IFile> runAPTDuringBuild(
final List<AnnotationProcessorFactory> factories, IFile file,
IJavaProject javaProj) {
ProcessorEnvImpl processorEnv = ProcessorEnvImpl
.newProcessorEnvironmentForBuild( file, javaProj);
return runAPT(factories, processorEnv);
}
/**
* Run annnotation processing.
* @param factories the list of annotation processor factories to be run.
* @return the set of files that need to be compiled.
*/
public static Set<IFile> runAPTDuringReconcile(
final List<AnnotationProcessorFactory> factories,
final CompilationUnit astCompilationUnit,
ICompilationUnit compilationUnit, IJavaProject javaProj) {
ProcessorEnvImpl processorEnv = ProcessorEnvImpl
.newProcessorEnvironmentForReconcile(astCompilationUnit,
compilationUnit, javaProj);
return runAPT(factories, processorEnv);
}
private static Set<IFile> runAPT(
final List<AnnotationProcessorFactory> factories,
final ProcessorEnvImpl processorEnv) {
try {
if (factories.size() == 0)
return Collections.emptySet();
if ( ! processorEnv.getFile().exists() )
return Collections.emptySet();
// clear out all the markers from the previous round.
final String markerType = processorEnv.getPhase() == ProcessorEnvImpl.Phase.RECONCILE ? ProcessorEnvImpl.RECONCILE_MARKER
: ProcessorEnvImpl.BUILD_MARKER;
try {
processorEnv.getFile().deleteMarkers(markerType, true,
IResource.DEPTH_INFINITE);
} catch (CoreException e) {
throw new IllegalStateException(e);
}
final Map<String, AnnotationTypeDeclaration> annotationDecls = getAnnotationTypeDeclarations(
processorEnv.getAstCompilationUnit(), processorEnv);
if (annotationDecls.isEmpty())
return Collections.emptySet();
for (int i = 0, size = factories.size(); i < size; i++) {
final AnnotationProcessorFactory factory = (AnnotationProcessorFactory) factories
.get(i);
final Set<AnnotationTypeDeclaration> factoryDecls = getAnnotations(
factory, annotationDecls);
if (factoryDecls != null && factoryDecls.size() > 0) {
final AnnotationProcessor processor = factory
.getProcessorFor(factoryDecls, processorEnv);
if (processor != null)
processor.process();
}
if (annotationDecls.isEmpty())
break;
}
// TODO: (theodora) log unclaimed annotations.
// notify the processor listeners
final Set<AnnotationProcessorListener> listeners = processorEnv
.getProcessorListeners();
for (AnnotationProcessorListener listener : listeners) {
EclipseRoundCompleteEvent event = null;
if (listener instanceof RoundCompleteListener) {
if (event == null)
event = new EclipseRoundCompleteEvent(processorEnv);
final RoundCompleteListener rcListener = (RoundCompleteListener) listener;
rcListener.roundComplete(event);
}
}
final Set<IFile> generatedFiles = new HashSet<IFile>();
generatedFiles.addAll( processorEnv.getGeneratedFiles() );
processorEnv.close();
return generatedFiles;
// log unclaimed annotations.
} catch (Throwable t) {
t.printStackTrace();
}
return Collections.emptySet();
}
/**
* invoking annotation processors respecting apt semantics.
*/
private static void checkAnnotations(
final List<AnnotationProcessorFactory> factories,
final Map<String, AnnotationTypeDeclaration> declarations,
final ProcessorEnvImpl env) {
for (int i = 0, size = factories.size(); i < size; i++) {
final AnnotationProcessorFactory factory = (AnnotationProcessorFactory) factories
.get(i);
final Set<AnnotationTypeDeclaration> factoryDecls = getAnnotations(
factory, declarations);
final AnnotationProcessor processor = factory.getProcessorFor(
factoryDecls, env);
processor.process();
if (declarations.isEmpty())
return;
}
// log unclaimed annotations.
}
private static Map<String, AnnotationTypeDeclaration> getAnnotationTypeDeclarations(
CompilationUnit astCompilationUnit, ProcessorEnvImpl env) {
final List<Annotation> instances = new ArrayList<Annotation>();
final AnnotationVisitor visitor = new AnnotationVisitor(instances);
astCompilationUnit.accept(new AnnotationVisitor(instances));
final Map<String, AnnotationTypeDeclaration> decls = new HashMap<String, AnnotationTypeDeclaration>();
for (int i = 0, size = instances.size(); i < size; i++) {
final Annotation instance = instances.get(i);
final ITypeBinding annoType = instance.resolveTypeBinding();
if (annoType == null)
continue;
final TypeDeclarationImpl annoDecl = Factory.createReferenceType(
annoType, env);
if (annoDecl.kind() == EclipseMirrorImpl.MirrorKind.TYPE_ANNOTATION)
decls.put(annoDecl.getQualifiedName(),
(AnnotationTypeDeclaration) annoDecl);
}
return decls;
}
/**
* @return the set of {@link AnnotationTypeDeclaration} that {@link #factory} supports or null
* if the factory doesn't support any of the declarations.
* If the factory supports "*", then the empty set will be returned
*
* This method will destructively modify {@link #declarations}. Entries will be removed from
* {@link #declarations} as the declarations are being added into the returned set.
*/
private static Set<AnnotationTypeDeclaration> getAnnotations(
final AnnotationProcessorFactory factory,
final Map<String, AnnotationTypeDeclaration> declarations)
{
final Collection<String> supportedTypes = factory
.supportedAnnotationTypes();
if (supportedTypes == null || supportedTypes.size() == 0)
return Collections.emptySet();
final Set<AnnotationTypeDeclaration> fDecls = new HashSet<AnnotationTypeDeclaration>();
for (Iterator<String> it = supportedTypes.iterator(); it.hasNext();) {
final String typeName = it.next();
if (typeName.equals("*")) {
declarations.clear();
return Collections.emptySet();
} else if (typeName.endsWith("*")) {
final String prefix = typeName.substring(0,
typeName.length() - 2);
for (Iterator<Map.Entry<String, AnnotationTypeDeclaration>> entries = declarations
.entrySet().iterator(); entries.hasNext();) {
final Map.Entry<String, AnnotationTypeDeclaration> entry = entries
.next();
final String key = entry.getKey();
if (key.startsWith(prefix)) {
fDecls
.add((AnnotationTypeDeclaration) entry
.getValue());
entries.remove();
}
}
} else {
final AnnotationTypeDeclaration decl = declarations
.get(typeName);
if (decl != null) {
fDecls.add(decl);
declarations.remove(typeName);
}
}
}
return fDecls.isEmpty() ? null : fDecls;
}
}