blob: 8fd372cf039322f1e5c600007d4e4c776232ab1a [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2005, 2007 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
*******************************************************************************/
package org.eclipse.jdt.apt.core.internal.env;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.jdt.apt.core.env.EclipseAnnotationProcessorEnvironment;
import org.eclipse.jdt.apt.core.env.Phase;
import org.eclipse.jdt.apt.core.internal.AptPlugin;
import org.eclipse.jdt.apt.core.internal.declaration.EclipseMirrorObject;
import org.eclipse.jdt.apt.core.internal.declaration.TypeDeclarationImpl;
import org.eclipse.jdt.apt.core.internal.env.MessagerImpl.Severity;
import org.eclipse.jdt.apt.core.internal.util.Factory;
import org.eclipse.jdt.apt.core.internal.util.Visitors.AnnotationVisitor;
import org.eclipse.jdt.apt.core.util.AptPreferenceConstants;
import org.eclipse.jdt.apt.core.util.EclipseMessager;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaConventions;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.compiler.BuildContext;
import org.eclipse.jdt.core.compiler.CategorizedProblem;
import org.eclipse.jdt.core.compiler.ReconcileContext;
import org.eclipse.jdt.core.dom.ASTRequestor;
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.AnnotationProcessorFactory;
import com.sun.mirror.apt.AnnotationProcessorListener;
import com.sun.mirror.apt.Filer;
import com.sun.mirror.declaration.AnnotationTypeDeclaration;
/** Base environment to be used during reconcile or build */
public abstract class AbstractCompilationEnv
extends BaseProcessorEnv
implements EclipseAnnotationProcessorEnvironment {
private Set<AnnotationProcessorListener> _listeners = null;
protected List<APTProblem> _problems = new ArrayList<APTProblem>();
private boolean _isClosed = false;
EnvCallback _callback;
private Set<IFile> _allGeneratedSourceFiles = new HashSet<IFile>();
private Set<IFile> _modifiedGeneratedSourceFiles = new HashSet<IFile>();
/**
* Currently open dom pipeline, used to request type bindings.
*/
protected ASTRequestor _requestor;
/**
* The processor that is currently being executed, or null if processing is not underway.
*/
private AnnotationProcessorFactory _currentProcessorFactory = null;
public static interface EnvCallback {
public void run(AbstractCompilationEnv env);
}
public static void newReconcileEnv(ReconcileContext reconcileContext, EnvCallback callback)
{
assert reconcileContext != null : "reconcile context is null"; //$NON-NLS-1$
ReconcileEnv env = ReconcileEnv.newEnv(reconcileContext);
env._callback = callback;
env.openPipeline();
}
public static void newBuildEnv(
BuildContext[] filesWithAnnotations,
final BuildContext[] additionalFiles,
IJavaProject javaProj,
EnvCallback callback)
{
assert filesWithAnnotations != null : "missing files"; //$NON-NLS-1$
// note, we are not reading any files.
BuildEnv env = new BuildEnv(filesWithAnnotations, additionalFiles, javaProj);
env._callback = callback;
env.createASTs(filesWithAnnotations);
}
AbstractCompilationEnv(
CompilationUnit compilationUnit,
IFile file,
IJavaProject javaProj,
Phase phase)
{
super(compilationUnit, file, javaProj, phase);
}
@Override
protected ITypeBinding getTypeBindingFromKey(String key, ICompilationUnit unit) {
return (ITypeBinding)_requestor.createBindings(new String[] {key})[0];
}
public void addListener(AnnotationProcessorListener listener)
{
checkValid();
if(_listeners == null )
_listeners = new HashSet<AnnotationProcessorListener>();
_listeners.add(listener);
}
public void removeListener(AnnotationProcessorListener listener)
{
checkValid();
if( _listeners == null ) return;
_listeners.remove(listener);
}
public Set<AnnotationProcessorListener> getProcessorListeners()
{
if( _listeners == null )
return Collections.emptySet();
// Return a copy, to avoid ConcurrentModificationException if a listener
// removes itself in response to the callback.
return new HashSet<AnnotationProcessorListener>(_listeners);
}
public Map<String, String> getOptions()
{
final HashMap<String, String> options = new HashMap<String, String>(_options);
options.put("phase", getPhase().toString()); //$NON-NLS-1$
return options;
}
abstract public CompilationUnit getASTFrom(final IFile file);
public CompilationUnit getAST(){
return _astRoot;
}
public EclipseMessager getMessager()
{
checkValid();
return new MessagerImpl(this);
}
abstract void addMessage(
IFile resource,
int start,
int end,
Severity severity,
String msg,
int line,
String[] arguments);
public List<? extends CategorizedProblem> getProblems(){
checkValid();
if( !_problems.isEmpty() )
EnvUtil.updateProblemLength(_problems, getAstCompilationUnit());
return _problems;
}
APTProblem createProblem(
IFile resource,
int start,
int end,
Severity severity,
String msg,
int line,
String[] arguments)
{
// end-1 since IProblem ending offsets are inclusive but DOM layer
// ending offsets are exclusive.
final APTProblem newProblem =
new APTProblem(msg, severity, resource, start, end-1, line, arguments);
return newProblem;
}
public abstract Filer getFiler();
public void addGeneratedSourceFile( IFile f, boolean contentsChanged ) {
if (!f.toString().endsWith(".java")) { //$NON-NLS-1$
throw new IllegalArgumentException("Source files must be java source files, and end with .java"); //$NON-NLS-1$
}
boolean addedToAll = _allGeneratedSourceFiles.add(f);
boolean addedToMod = false;
if (contentsChanged)
addedToMod = _modifiedGeneratedSourceFiles.add(f);
if (AptPlugin.DEBUG_COMPILATION_ENV) {
AptPlugin.trace("add generated file " + f + " to env " + this + //$NON-NLS-1$ //$NON-NLS-2$
"; addToAll = " + addedToAll + "; addToMod = " + addedToMod + //$NON-NLS-1$ //$NON-NLS-2$
"; contentsChanged = " + contentsChanged); //$NON-NLS-1$
}
}
public void addGeneratedNonSourceFile(final IFile file) {
_allGeneratedSourceFiles.add(file);
}
public Set<IFile> getAllGeneratedFiles() {
return _allGeneratedSourceFiles;
}
public Set<IFile> getModifiedGeneratedFiles() {
return _modifiedGeneratedSourceFiles;
}
/**
* @return true iff source files has been generated.
* Always return false when this environment is closed.
*/
public boolean hasGeneratedSourceFiles(){ return !_allGeneratedSourceFiles.isEmpty(); }
/**
* @return all annotation types in the current compilation unit.
*/
public Map<String, AnnotationTypeDeclaration> getAnnotationTypes()
{
checkValid();
final List<Annotation> instances = new ArrayList<Annotation>();
final Map<String, AnnotationTypeDeclaration> decls =
new HashMap<String, AnnotationTypeDeclaration>();
final AnnotationVisitor visitor = new AnnotationVisitor(instances);
_astRoot.accept(visitor);
for (int instanceIndex=0, size = instances.size(); instanceIndex < size; instanceIndex++) {
final Annotation instance = instances.get(instanceIndex);
final ITypeBinding annoType = instance.resolveTypeBinding();
if (annoType == null)
continue;
final TypeDeclarationImpl decl =
Factory.createReferenceType(annoType, this);
if (decl.kind() == EclipseMirrorObject.MirrorKind.TYPE_ANNOTATION){
final AnnotationTypeDeclaration annoDecl = (AnnotationTypeDeclaration)decl;
decls.put(annoDecl.getQualifiedName(), annoDecl);
}
}
return decls;
}
/* package */ void checkValid()
{
if( _isClosed )
throw new IllegalStateException("Environment has expired"); //$NON-NLS-1$
}
// Call this after each file; cf. BuildEnv#beginFileProcessing()
protected void completedProcessing() {
_modifiedGeneratedSourceFiles.clear();
}
public void close(){
if (isClosed())
return;
if(_listeners != null)
_listeners.clear();
_problems = null;
_typeCache.clear();
_packageRootsCache = null;
_isClosed = true;
_callback = null;
_requestor = null;
_allGeneratedSourceFiles = null;
_modifiedGeneratedSourceFiles = null;
if (AptPlugin.DEBUG_COMPILATION_ENV) AptPlugin.trace(
"closed env " + this); //$NON-NLS-1$
}
boolean isClosed(){ return _isClosed; }
/**
* Check typeName to ensure it doesn't contain any bogus characters.
* @param typeName
* @throws CoreException
*/
@SuppressWarnings("unchecked")
public void validateTypeName(String typeName) throws CoreException
{
Map<String, String> options = getJavaProject().getOptions(true);
String sourceLevel = options.get(JavaCore.COMPILER_SOURCE);
String complianceLevel = options.get(JavaCore.COMPILER_COMPLIANCE);
IStatus status = JavaConventions.validateJavaTypeName(typeName, sourceLevel, complianceLevel);
if (status.matches(IStatus.ERROR)) {
throw new CoreException(status);
}
}
public AnnotationProcessorFactory getCurrentProcessorFactory() {
return _currentProcessorFactory;
}
public void setCurrentProcessorFactory(AnnotationProcessorFactory processor)
{
_currentProcessorFactory = processor;
}
public boolean currentProcessorSupportsRTTG()
{
AnnotationProcessorFactory factory = getCurrentProcessorFactory();
if (null == factory) {
return false;
}
Collection<String> options = factory.supportedOptions();
if (null == options) {
return false;
}
return options.contains(AptPreferenceConstants.RTTG_ENABLED_OPTION);
}
}