/*******************************************************************************
* Copyright (c) 2011 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.compiler.apt.tests.processors.inherited;
import java.util.ArrayList;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.TypeElement;
import javax.annotation.processing.*;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.*;
import javax.lang.model.type.*;
import javax.lang.model.util.SimpleTypeVisitor6;
import javax.lang.model.util.Types;
import javax.tools.Diagnostic.Kind;
import org.eclipse.jdt.compiler.apt.tests.annotations.ArgsConstructor;
import org.eclipse.jdt.compiler.apt.tests.processors.base.BaseProcessor;
@SupportedAnnotationTypes("org.eclipse.jdt.compiler.apt.tests.annotations.ArgsConstructor")
@SupportedSourceVersion(SourceVersion.RELEASE_6)
public class ArgsConstructorProcessor extends BaseProcessor {
private TypeElement _elementAC;
@Override
public boolean process(Set<? extends TypeElement> annotations,
RoundEnvironment env) {
for (TypeElement type : annotations) {
processArgsConstructorClasses(env, type);
}
if (!collectElements()) {
reportError("Failed");
return false;
}
TypeMirror superclass = _elementAC.getSuperclass();
if (TypeKind.DECLARED != superclass.getKind()) {
reportError("Wrong type: should be a declared type");
return false;
}
Element typeElement = _typeUtils.asElement(superclass);
if (typeElement.getAnnotationMirrors().size() != 1) {
reportError("Should contain an annotation");
return false;
}
reportSuccess();
return true;
}
/**
* Collect some elements that will be reused in various tests
* @return true if all tests passed
*/
private boolean collectElements() {
_elementAC = _elementUtils.getTypeElement("targets.inherited.TestGenericChild");
if (_elementAC == null) {
reportError("TestGenericChild was not found");
return false;
}
return true;
}
private void processArgsConstructorClasses(RoundEnvironment env,
TypeElement type) {
for (Element element : env.getElementsAnnotatedWith(type)) {
processClass(element);
processingEnv.getMessager().printMessage(Kind.NOTE,
"Class " + element + " is processed");
}
}
private void processClass(Element element) {
String actionName = ArgsConstructor.class.getName();
AnnotationValue action = null;
for (AnnotationMirror am : processingEnv.getElementUtils()
.getAllAnnotationMirrors(element)) {
if (actionName.equals(am.getAnnotationType().toString())) {
for (Entry<? extends ExecutableElement, ? extends AnnotationValue> entry : am
.getElementValues().entrySet()) {
if ("value".equals(entry.getKey().getSimpleName()
.toString())) {
action = entry.getValue();
break;
}
}
}
}
if (action == null) {
processingEnv.getMessager()
.printMessage(
Kind.WARNING,
"Class " + element
+ " lacks a annotation with required args",
element);
return;
}
List<TypeMirror> mirrors = new ArrayList<TypeMirror>();
for (Object val : (List<?>) action.getValue()) {
AnnotationValue v = (AnnotationValue) val;
TypeMirror m = (TypeMirror) v.getValue();
mirrors.add(m);
}
if (!doesClassContainArgsConstructor(element, mirrors)) {
String s = "";
for (TypeMirror tm : mirrors) {
if (!s.isEmpty()) {
s += ",";
}
s += tm.toString();
}
processingEnv.getMessager().printMessage(
Kind.ERROR,
"Class " + element
+ " lacks a public constructor with args: " + s,
element);
} else {
processingEnv.getMessager().printMessage(Kind.NOTE,
"Processed type: " + element);
}
}
private boolean doesClassContainArgsConstructor(Element el,
List<TypeMirror> annotTypes) {
for (Element subelement : el.getEnclosedElements()) {
if (subelement.getKind() == ElementKind.CONSTRUCTOR
&& subelement.getModifiers().contains(Modifier.PUBLIC)) {
TypeMirror mirror = subelement.asType();
if (mirror.accept(argsVisitor, annotTypes))
return true;
}
}
return false;
}
private final TypeVisitor<Boolean, List<TypeMirror>> argsVisitor = new SimpleTypeVisitor6<Boolean, List<TypeMirror>>() {
public Boolean visitExecutable(ExecutableType t,
List<TypeMirror> annotatedTypes) {
List<? extends TypeMirror> types = t.getParameterTypes();
if (annotatedTypes.size() != types.size()) {
return false;
}
Types tutil = processingEnv.getTypeUtils();
for (int i = 0; i < types.size(); i++) {
TypeMirror test = tutil.erasure(types.get(i));// because same
// type bad
// Map<String,String>
// != Map
TypeMirror expected = tutil.erasure(annotatedTypes.get(i));
if (!tutil.isAssignable(expected, test)) {
return false;
}
}
return true;
}
};
}