com.googlecode.androidannotations.helper.ValidatorHelper Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of androidannotations Show documentation
Show all versions of androidannotations Show documentation
The Annotation Processor jar that generates code
The newest version!
/**
* Copyright (C) 2010-2012 eBusiness Information, Excilys Group
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed To in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.googlecode.androidannotations.helper;
import static com.googlecode.androidannotations.helper.AndroidConstants.LOG_DEBUG;
import static com.googlecode.androidannotations.helper.AndroidConstants.LOG_ERROR;
import static com.googlecode.androidannotations.helper.AndroidConstants.LOG_INFO;
import static com.googlecode.androidannotations.helper.AndroidConstants.LOG_VERBOSE;
import static com.googlecode.androidannotations.helper.AndroidConstants.LOG_WARN;
import static com.googlecode.androidannotations.helper.CanonicalNameConstants.HTTP_MESSAGE_CONVERTER;
import static com.googlecode.androidannotations.helper.ModelConstants.GENERATION_SUFFIX;
import static java.util.Arrays.asList;
import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.ArrayType;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.ErrorType;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.ElementFilter;
import javax.lang.model.util.Elements;
import com.googlecode.androidannotations.annotations.EActivity;
import com.googlecode.androidannotations.annotations.EApplication;
import com.googlecode.androidannotations.annotations.EBean;
import com.googlecode.androidannotations.annotations.EFragment;
import com.googlecode.androidannotations.annotations.EProvider;
import com.googlecode.androidannotations.annotations.EReceiver;
import com.googlecode.androidannotations.annotations.EService;
import com.googlecode.androidannotations.annotations.EView;
import com.googlecode.androidannotations.annotations.EViewGroup;
import com.googlecode.androidannotations.annotations.Trace;
import com.googlecode.androidannotations.annotations.ViewById;
import com.googlecode.androidannotations.annotations.rest.Delete;
import com.googlecode.androidannotations.annotations.rest.Get;
import com.googlecode.androidannotations.annotations.rest.Head;
import com.googlecode.androidannotations.annotations.rest.Options;
import com.googlecode.androidannotations.annotations.rest.Post;
import com.googlecode.androidannotations.annotations.rest.Put;
import com.googlecode.androidannotations.annotations.rest.Rest;
import com.googlecode.androidannotations.annotations.sharedpreferences.DefaultBoolean;
import com.googlecode.androidannotations.annotations.sharedpreferences.DefaultFloat;
import com.googlecode.androidannotations.annotations.sharedpreferences.DefaultInt;
import com.googlecode.androidannotations.annotations.sharedpreferences.DefaultLong;
import com.googlecode.androidannotations.annotations.sharedpreferences.DefaultString;
import com.googlecode.androidannotations.annotations.sharedpreferences.SharedPref;
import com.googlecode.androidannotations.api.sharedpreferences.SharedPreferencesHelper;
import com.googlecode.androidannotations.model.AndroidSystemServices;
import com.googlecode.androidannotations.model.AnnotationElements;
import com.googlecode.androidannotations.processing.InstanceStateProcessor;
import com.googlecode.androidannotations.validation.IsValid;
public class ValidatorHelper {
private static final List ANDROID_SHERLOCK_MENU_ITEM_QUALIFIED_NAMES = asList(CanonicalNameConstants.MENU_ITEM, CanonicalNameConstants.SHERLOCK_MENU_ITEM);
private static final List ANDROID_FRAGMENT_QUALIFIED_NAMES = asList(CanonicalNameConstants.FRAGMENT, CanonicalNameConstants.SUPPORT_V4_FRAGMENT);
private static final String METHOD_NAME_SET_ROOT_URL = "setRootUrl";
private static final List VALID_PREF_RETURN_TYPES = Arrays.asList("int", "boolean", "float", "long", CanonicalNameConstants.STRING);
private static final List INVALID_PREF_METHOD_NAMES = Arrays.asList("edit", "getSharedPreferences", "clear", "getEditor", "apply");
private static final Collection VALID_LOG_LEVELS = Arrays.asList(LOG_VERBOSE, LOG_DEBUG, LOG_INFO, LOG_WARN, LOG_ERROR);
@SuppressWarnings("unchecked")
private static final List> VALID_ENHANCED_VIEW_SUPPORT_ANNOTATIONS = asList(EActivity.class, EViewGroup.class, EView.class, EBean.class, EFragment.class);
@SuppressWarnings("unchecked")
private static final List> VALID_ENHANCED_COMPONENT_ANNOTATIONS = asList(EApplication.class, EActivity.class, EViewGroup.class, EView.class, EBean.class, EService.class, EReceiver.class, EProvider.class, EFragment.class);
protected final TargetAnnotationHelper annotationHelper;
public ValidatorHelper(TargetAnnotationHelper targetAnnotationHelper) {
annotationHelper = targetAnnotationHelper;
}
public void isNotFinal(Element element, IsValid valid) {
if (annotationHelper.isFinal(element)) {
valid.invalidate();
annotationHelper.printAnnotationError(element, "%s cannot be used on a final element");
}
}
public void isNotSynchronized(Element element, IsValid valid) {
if (annotationHelper.isSynchronized(element)) {
valid.invalidate();
annotationHelper.printAnnotationError(element, "%s cannot be used on a synchronized element. If you think you shall need to use the synchronized keyword for a specific use case, please post on the mailing list.");
}
}
public void isInterface(TypeElement element, IsValid valid) {
if (!annotationHelper.isInterface(element)) {
valid.invalidate();
annotationHelper.printAnnotationError(element, "%s can only be used on an interface");
}
}
public void isTopLevel(TypeElement element, IsValid valid) {
if (!annotationHelper.isTopLevel(element)) {
valid.invalidate();
annotationHelper.printAnnotationError(element, "%s can only be used on a top level type");
}
}
public void doesNotExtendOtherInterfaces(TypeElement element, IsValid valid) {
if (element.getInterfaces().size() > 0) {
valid.invalidate();
annotationHelper.printAnnotationError(element, "%s can only be used on an interface that does not extend other interfaces");
}
}
public void doesNotReturnPrimitive(ExecutableElement element, IsValid valid) {
if (element.getReturnType().getKind().isPrimitive()) {
valid.invalidate();
annotationHelper.printAnnotationError(element, "%s cannot return primitive");
}
}
public void doesNotReturnArray(ExecutableElement element, IsValid valid) {
if (element.getReturnType().getKind() == TypeKind.ARRAY) {
valid.invalidate();
annotationHelper.printAnnotationError(element, "%s cannot return array");
}
}
public void isNotPrivate(Element element, IsValid valid) {
if (annotationHelper.isPrivate(element)) {
valid.invalidate();
annotationHelper.printAnnotationError(element, "%s cannot be used on a private element");
}
}
public void enclosingElementHasEBeanAnnotation(Element element, AnnotationElements validatedElements, IsValid valid) {
Element enclosingElement = element.getEnclosingElement();
hasClassAnnotation(element, enclosingElement, validatedElements, EBean.class, valid);
}
public void enclosingElementHasEActivity(Element element, AnnotationElements validatedElements, IsValid valid) {
Element enclosingElement = element.getEnclosingElement();
hasClassAnnotation(element, enclosingElement, validatedElements, EActivity.class, valid);
}
public void enclosingElementHasEActivityOrEFragment(Element element, AnnotationElements validatedElements, IsValid valid) {
Element enclosingElement = element.getEnclosingElement();
@SuppressWarnings("unchecked")
List> validAnnotations = asList(EActivity.class, EFragment.class);
hasOneOfClassAnnotations(element, enclosingElement, validatedElements, validAnnotations, valid);
}
public void enclosingElementHasEFragment(Element element, AnnotationElements validatedElements, IsValid valid) {
Element enclosingElement = element.getEnclosingElement();
hasClassAnnotation(element, enclosingElement, validatedElements, EFragment.class, valid);
}
public void hasEActivity(Element element, AnnotationElements validatedElements, IsValid valid) {
hasClassAnnotation(element, element, validatedElements, EActivity.class, valid);
}
public void hasEActivityOrEFragment(Element element, AnnotationElements validatedElements, IsValid valid) {
@SuppressWarnings("unchecked")
List> validAnnotations = asList(EActivity.class, EFragment.class);
hasOneOfClassAnnotations(element, element, validatedElements, validAnnotations, valid);
}
public void enclosingElementHasEnhancedViewSupportAnnotation(Element element, AnnotationElements validatedElements, IsValid valid) {
Element enclosingElement = element.getEnclosingElement();
hasOneOfClassAnnotations(element, enclosingElement, validatedElements, VALID_ENHANCED_VIEW_SUPPORT_ANNOTATIONS, valid);
}
public void enclosingElementHasEnhancedComponentAnnotation(Element element, AnnotationElements validatedElements, IsValid valid) {
Element enclosingElement = element.getEnclosingElement();
hasOneOfClassAnnotations(element, enclosingElement, validatedElements, VALID_ENHANCED_COMPONENT_ANNOTATIONS, valid);
}
private void hasClassAnnotation(Element reportElement, Element element, AnnotationElements validatedElements, Class extends Annotation> validAnnotation, IsValid valid) {
ArrayList> validAnnotations = new ArrayList>();
validAnnotations.add(validAnnotation);
hasOneOfClassAnnotations(reportElement, element, validatedElements, validAnnotations, valid);
}
private void hasOneOfClassAnnotations(Element reportElement, Element element, AnnotationElements validatedElements, List> validAnnotations, IsValid valid) {
boolean foundAnnotation = false;
for (Class extends Annotation> validAnnotation : validAnnotations) {
if (element.getAnnotation(validAnnotation) != null) {
Set extends Element> layoutAnnotatedElements = validatedElements.getRootAnnotatedElements(validAnnotation.getName());
/*
* This is for the case where the element has the right
* annotation, but that annotation was not validated. We do not
* add any compile error (should already exist on the
* annotation), but we still invalidate this element.
*/
if (!layoutAnnotatedElements.contains(element)) {
valid.invalidate();
}
foundAnnotation = true;
break;
}
}
if (!foundAnnotation) {
valid.invalidate();
annotationHelper.printAnnotationError(reportElement, "%s can only be used in a class annotated with " + getFormattedValidEnhancedBeanAnnotationTypes(validAnnotations) + ".");
}
}
private String getFormattedValidEnhancedBeanAnnotationTypes(List> annotations) {
StringBuilder sb = new StringBuilder();
if (!annotations.isEmpty()) {
sb.append(TargetAnnotationHelper.annotationName(annotations.get(0)));
for (int i = 1; i < annotations.size(); i++) {
sb.append(", ");
sb.append(TargetAnnotationHelper.annotationName(annotations.get(i)));
}
}
return sb.toString();
}
public void hasViewByIdAnnotation(Element element, AnnotationElements validatedElements, IsValid valid) {
String error = "can only be used with annotation";
elementHasAnnotation(ViewById.class, element, validatedElements, valid, error);
}
public void elementHasRestAnnotationOrEnclosingElementHasRestAnnotationAndElementHasMethodRestAnnotation(Element element, AnnotationElements validatedElements, IsValid valid) {
String error = "can only be used in an interface annotated with";
elementHasAnnotation(Rest.class, element, validatedElements, valid, error);
if (!valid.isValid()) {
enclosingElementHasRestAnnotation(element, validatedElements, valid);
elementHasMethodRestAnnotation(element, validatedElements, valid);
}
}
public void elementHasMethodRestAnnotation(Element element, AnnotationElements validatedElements, IsValid valid) {
String error = "can only be used on a method annotated with Rest methods.";
elementHasAnnotationContainsIn(REST_ANNOTATION_CLASSES, element, validatedElements, valid, error);
}
public void enclosingElementHasRestAnnotation(Element element, AnnotationElements validatedElements, IsValid valid) {
String error = "can only be used in an interface annotated with";
enclosingElementHasAnnotation(Rest.class, element, validatedElements, valid, error);
}
public void enclosingElementHasAnnotation(Class extends Annotation> annotation, Element element, AnnotationElements validatedElements, IsValid valid, String error) {
Element enclosingElement = element.getEnclosingElement();
elementHasAnnotation(annotation, enclosingElement, validatedElements, valid, error);
}
public void elementHasAnnotation(Class extends Annotation> annotation, Element element, AnnotationElements validatedElements, IsValid valid, String error) {
Set extends Element> layoutAnnotatedElements = validatedElements.getRootAnnotatedElements(annotation.getName());
if (!layoutAnnotatedElements.contains(element)) {
valid.invalidate();
if (element.getAnnotation(annotation) == null) {
annotationHelper.printAnnotationError(element, "%s " + error + " " + TargetAnnotationHelper.annotationName(annotation));
}
}
}
public void elementHasAnnotationContainsIn(List> annotations, Element element, AnnotationElements validatedElements, IsValid valid, String error) {
boolean isAnnoted = false;
for (Class extends Annotation> annotation : annotations) {
if (elementHasAnnotation(annotation, element, validatedElements)) {
isAnnoted = true;
break;
}
}
if (!isAnnoted) {
valid.invalidate();
annotationHelper.printAnnotationError(element, "%s " + error);
}
}
public boolean elementHasAnnotation(Class extends Annotation> annotation, Element element, AnnotationElements validatedElements) {
Set extends Element> layoutAnnotatedElements = validatedElements.getRootAnnotatedElements(annotation.getName());
return layoutAnnotatedElements.contains(element);
}
public void throwsOnlyRestClientException(ExecutableElement element, IsValid valid) {
List extends TypeMirror> thrownTypes = element.getThrownTypes();
if (thrownTypes.size() > 0) {
if (thrownTypes.size() > 1 || !thrownTypes.get(0).toString().equals("org.springframework.web.client.RestClientException")) {
valid.invalidate();
annotationHelper.printAnnotationError(element, "%s annotated methods can only declare throwing a RestClientException");
}
}
}
public void elementHasGetOrPostAnnotation(Element element, AnnotationElements validatedElements, IsValid valid) {
if (!elementHasAnnotation(Get.class, element) && !elementHasAnnotation(Post.class, element)) {
annotationHelper.printAnnotationError(element, "%s can only be used in an interface annotated with Get or Post annotation");
}
}
public void typeHasAnnotation(Class extends Annotation> annotation, Element element, IsValid valid) {
TypeMirror elementType = element.asType();
typeHasAnnotation(annotation, elementType, element, valid);
}
public void typeHasAnnotation(Class extends Annotation> annotation, TypeMirror elementType, Element reportingElement, IsValid valid) {
Element typeElement = annotationHelper.getTypeUtils().asElement(elementType);
if (!elementHasAnnotationSafe(annotation, typeElement)) {
valid.invalidate();
annotationHelper.printAnnotationError(reportingElement, "%s can only be used on an element annotated with " + TargetAnnotationHelper.annotationName(annotation));
}
}
public void typeOrTargetValueHasAnnotation(Class extends Annotation> annotation, Element element, IsValid valid) {
DeclaredType targetAnnotationClassValue = annotationHelper.extractAnnotationClassParameter(element);
if (targetAnnotationClassValue != null) {
typeHasAnnotation(annotation, targetAnnotationClassValue, element, valid);
if (!annotationHelper.getTypeUtils().isAssignable(targetAnnotationClassValue, element.asType())) {
valid.invalidate();
annotationHelper.printAnnotationError(element, "The value of %s must be assignable into the annotated field");
}
} else {
typeHasAnnotation(annotation, element, valid);
}
}
private boolean elementHasAnnotationSafe(Class extends Annotation> annotation, Element element) {
List extends AnnotationMirror> annotationMirrors = element.getAnnotationMirrors();
for (AnnotationMirror annotationMirror : annotationMirrors) {
if (annotationMirror.getAnnotationType().toString().equals(annotation.getName())) {
return true;
}
}
return false;
}
private boolean elementHasAnnotation(Class extends Annotation> annotation, Element element) {
return element.getAnnotation(annotation) != null;
}
public void elementHasRestAnnotation(Element element, AnnotationElements validatedElements, IsValid valid) {
String error = "can only be used in an interface annotated with";
elementHasAnnotation(Rest.class, element, validatedElements, valid, error);
}
public void returnTypeNotGenericUnlessResponseEntity(ExecutableElement element, IsValid valid) {
TypeMirror returnType = element.getReturnType();
TypeKind returnKind = returnType.getKind();
if (returnKind == TypeKind.DECLARED) {
DeclaredType declaredReturnType = (DeclaredType) returnType;
if (!declaredReturnType.toString().startsWith("org.springframework.http.ResponseEntity<") && declaredReturnType.getTypeArguments().size() > 0) {
valid.invalidate();
annotationHelper.printAnnotationError(element, "%s annotated methods cannot return parameterized types, except for ResponseEntity");
}
}
}
public void hasHttpHeadersReturnType(ExecutableElement element, IsValid valid) {
String returnType = element.getReturnType().toString();
if (!returnType.equals("org.springframework.http.HttpHeaders")) {
valid.invalidate();
annotationHelper.printAnnotationError(element, "%s annotated methods can only return a HttpHeaders, not " + returnType);
}
}
public void hasSetOfHttpMethodReturnType(ExecutableElement element, IsValid valid) {
TypeMirror returnType = element.getReturnType();
String returnTypeString = returnType.toString();
if (!returnTypeString.equals("java.util.Set")) {
valid.invalidate();
annotationHelper.printAnnotationError(element, "%s annotated methods can only return a Set of HttpMethod, not " + returnTypeString);
} else {
DeclaredType declaredType = (DeclaredType) returnType;
List extends TypeMirror> typeArguments = declaredType.getTypeArguments();
if (typeArguments.size() != 1) {
valid.invalidate();
annotationHelper.printAnnotationError(element, "%s annotated methods can only return a parameterized Set (with HttpMethod)");
} else {
TypeMirror typeArgument = typeArguments.get(0);
if (!typeArgument.toString().equals("org.springframework.http.HttpMethod")) {
valid.invalidate();
annotationHelper.printAnnotationError(element, "%s annotated methods can only return a parameterized Set of HttpMethod, not " + typeArgument.toString());
}
}
}
}
public void urlVariableNamesExistInParameters(ExecutableElement element, List variableNames, IsValid valid) {
List extends VariableElement> parameters = element.getParameters();
List parametersName = new ArrayList();
for (VariableElement parameter : parameters) {
parametersName.add(parameter.getSimpleName().toString());
}
for (String variableName : variableNames) {
if (!parametersName.contains(variableName)) {
valid.invalidate();
annotationHelper.printAnnotationError(element, "%s annotated method has an url variable which name could not be found in the method parameters: " + variableName);
return;
}
}
}
public void doesntThrowException(Element element, IsValid valid) {
ExecutableElement executableElement = (ExecutableElement) element;
if (executableElement.getThrownTypes().size() > 0) {
valid.invalidate();
annotationHelper.printAnnotationError(element, "%s annotated methods should not declare throwing any exception");
}
}
public void returnTypeIsVoidOrBoolean(ExecutableElement executableElement, IsValid valid) {
TypeMirror returnType = executableElement.getReturnType();
TypeKind returnKind = returnType.getKind();
if (returnKind != TypeKind.BOOLEAN && returnKind != TypeKind.VOID && !returnType.toString().equals("java.lang.Boolean")) {
valid.invalidate();
annotationHelper.printAnnotationError(executableElement, "%s can only be used on a method with a boolean or a void return type");
}
}
public void returnTypeIsVoid(ExecutableElement executableElement, IsValid valid) {
TypeMirror returnType = executableElement.getReturnType();
if (returnType.getKind() != TypeKind.VOID) {
valid.invalidate();
annotationHelper.printAnnotationError(executableElement, "%s can only be used on a method with a void return type");
}
}
public void zeroOrOneParameter(ExecutableElement executableElement, IsValid valid) {
List extends VariableElement> parameters = executableElement.getParameters();
if (parameters.size() > 1) {
valid.invalidate();
annotationHelper.printAnnotationError(executableElement, "%s can only be used on a method with zero or one parameter, instead of " + parameters.size());
}
}
public void zeroParameter(ExecutableElement executableElement, IsValid valid) {
List extends VariableElement> parameters = executableElement.getParameters();
if (parameters.size() > 0) {
valid.invalidate();
annotationHelper.printAnnotationError(executableElement, "%s can only be used on a method with zero parameter, instead of " + parameters.size());
}
}
public void zeroOrOneViewParameters(ExecutableElement executableElement, IsValid valid) {
zeroOrOneSpecificParameter(executableElement, CanonicalNameConstants.VIEW, valid);
}
public void zeroOrOneMenuItemParameters(ExecutableElement executableElement, IsValid valid) {
zeroOrOneSpecificParameter(executableElement, ANDROID_SHERLOCK_MENU_ITEM_QUALIFIED_NAMES, valid);
}
public void zeroOrOneSpecificParameter(ExecutableElement executableElement, String parameterTypeQualifiedName, IsValid valid) {
zeroOrOneSpecificParameter(executableElement, Arrays.asList(parameterTypeQualifiedName), valid);
}
public void zeroOrOneSpecificParameter(ExecutableElement executableElement, List parameterTypeQualifiedNames, IsValid valid) {
zeroOrOneParameter(executableElement, valid);
List extends VariableElement> parameters = executableElement.getParameters();
if (parameters.size() == 1) {
VariableElement parameter = parameters.get(0);
TypeMirror parameterType = parameter.asType();
if (!parameterTypeQualifiedNames.contains(parameterType.toString())) {
valid.invalidate();
annotationHelper.printAnnotationError(executableElement, "%s can only be used on a method with no parameter or a parameter of type " + parameterTypeQualifiedNames + ", not " + parameterType);
}
}
}
public void zeroOrOneBundleParameter(ExecutableElement executableElement, IsValid valid) {
zeroOrOneSpecificParameter(executableElement, CanonicalNameConstants.BUNDLE, valid);
}
public void extendsActivity(Element element, IsValid valid) {
extendsType(element, CanonicalNameConstants.ACTIVITY, valid);
}
public void extendsFragment(Element element, IsValid valid) {
extendsOneOfTypes(element, ANDROID_FRAGMENT_QUALIFIED_NAMES, valid);
}
public void extendsService(Element element, IsValid valid) {
extendsType(element, CanonicalNameConstants.SERVICE, valid);
}
public void extendsReceiver(Element element, IsValid valid) {
extendsType(element, CanonicalNameConstants.BROADCAST_RECEIVER, valid);
}
public void extendsProvider(Element element, IsValid valid) {
extendsType(element, CanonicalNameConstants.CONTENT_PROVIDER, valid);
}
public void extendsView(Element element, IsValid valid) {
extendsType(element, CanonicalNameConstants.VIEW, valid);
}
public void extendsTextView(Element element, IsValid valid) {
extendsType(element, CanonicalNameConstants.TEXT_VIEW, valid);
}
public void extendsViewGroup(Element element, IsValid valid) {
extendsType(element, CanonicalNameConstants.VIEW_GROUP, valid);
}
public void extendsApplication(Element element, IsValid valid) {
extendsType(element, CanonicalNameConstants.APPLICATION, valid);
}
public void extendsContext(Element element, IsValid valid) {
extendsType(element, CanonicalNameConstants.CONTEXT, valid);
}
public void extendsOrmLiteDaoWithValidModelParameter(Element element, IsValid valid) {
TypeMirror elementType = element.asType();
TypeMirror modelTypeMirror = annotationHelper.extractAnnotationParameter(element, "model");
TypeElement daoTypeElement = annotationHelper.typeElementFromQualifiedName(CanonicalNameConstants.DAO);
if (daoTypeElement != null) {
TypeMirror wildcardType = annotationHelper.getTypeUtils().getWildcardType(null, null);
DeclaredType daoParameterizedType = annotationHelper.getTypeUtils().getDeclaredType(daoTypeElement, modelTypeMirror, wildcardType);
// Checks that elementType extends Dao
if (!annotationHelper.isSubtype(elementType, daoParameterizedType)) {
valid.invalidate();
annotationHelper.printAnnotationError(element, "%s can only be used on an element that extends " + daoParameterizedType.toString());
}
}
}
public void hasASqlLiteOpenHelperParameterizedType(Element element, IsValid valid) {
TypeMirror helperType = annotationHelper.extractAnnotationParameter(element, "helper");
TypeMirror openHelperType = annotationHelper.typeElementFromQualifiedName(CanonicalNameConstants.SQLLITE_OPEN_HELPER).asType();
if (!annotationHelper.isSubtype(helperType, openHelperType)) {
valid.invalidate();
annotationHelper.printAnnotationError(element, "%s helper() parameter must extend " + CanonicalNameConstants.SQLLITE_OPEN_HELPER);
}
}
public void upperclassOfRegisteredApplication(Element element, AndroidManifest manifest, IsValid valid) {
if (manifest.isLibraryProject()) {
return;
}
String applicationClassName = manifest.getApplicationClassName();
if (applicationClassName != null) {
if (applicationClassName.endsWith(GENERATION_SUFFIX)) {
applicationClassName = applicationClassName.substring(0, applicationClassName.length() - GENERATION_SUFFIX.length());
}
TypeMirror elementType = element.asType();
TypeMirror manifestType = annotationHelper.typeElementFromQualifiedName(applicationClassName).asType();
if (!annotationHelper.isSubtype(manifestType, elementType)) {
valid.invalidate();
annotationHelper.printAnnotationError(element, "%s can only be used on an element that is an instance of the following class (or one of it's superclass): " + applicationClassName);
}
} else {
valid.invalidate();
annotationHelper.printAnnotationError(element, "No application class is registered in the AndroidManifest.xml");
}
}
public void applicationRegistered(Element element, AndroidManifest manifest, IsValid valid) {
if (manifest.isLibraryProject()) {
return;
}
String applicationClassName = manifest.getApplicationClassName();
if (applicationClassName != null) {
TypeElement typeElement = (TypeElement) element;
String componentQualifiedName = typeElement.getQualifiedName().toString();
String generatedComponentQualifiedName = componentQualifiedName + ModelConstants.GENERATION_SUFFIX;
if (!applicationClassName.equals(generatedComponentQualifiedName)) {
if (applicationClassName.equals(componentQualifiedName)) {
valid.invalidate();
annotationHelper.printAnnotationError(element, "The AndroidManifest.xml file contains the original component, and not the AndroidAnnotations generated component. Please register " + generatedComponentQualifiedName + " instead of " + componentQualifiedName);
} else {
annotationHelper.printAnnotationWarning(element, "The component " + generatedComponentQualifiedName + " is not registered in the AndroidManifest.xml file.");
}
}
} else {
valid.invalidate();
annotationHelper.printAnnotationError(element, "No application class registered in the AndroidManifest.xml");
}
}
public void isSharedPreference(Element element, AnnotationElements validatedElements, IsValid valid) {
TypeMirror type = element.asType();
/*
* The type is not available yet because it has just been generated
*/
if (type instanceof ErrorType) {
String elementTypeName = type.toString();
boolean sharedPrefValidatedInRound = false;
if (elementTypeName.endsWith(GENERATION_SUFFIX)) {
String prefTypeName = elementTypeName.substring(0, elementTypeName.length() - GENERATION_SUFFIX.length());
Set extends Element> sharedPrefElements = validatedElements.getRootAnnotatedElements(SharedPref.class.getName());
for (Element sharedPrefElement : sharedPrefElements) {
TypeElement sharedPrefTypeElement = (TypeElement) sharedPrefElement;
String sharedPrefQualifiedName = sharedPrefTypeElement.getQualifiedName().toString();
if (sharedPrefQualifiedName.endsWith(prefTypeName)) {
sharedPrefValidatedInRound = true;
break;
}
}
}
if (!sharedPrefValidatedInRound) {
valid.invalidate();
}
} else {
extendsType(element, SharedPreferencesHelper.class.getName(), valid);
}
}
public void extendsOneOfTypes(Element element, List typeQualifiedNames, IsValid valid) {
TypeMirror elementType = element.asType();
for (String typeQualifiedName : typeQualifiedNames) {
TypeElement typeElement = annotationHelper.typeElementFromQualifiedName(typeQualifiedName);
if (typeElement != null) {
TypeMirror expectedType = typeElement.asType();
if (annotationHelper.isSubtype(elementType, expectedType)) {
return;
}
}
}
valid.invalidate();
annotationHelper.printAnnotationError(element, "%s can only be used on an element that extends one of the following classes: " + typeQualifiedNames);
}
public void extendsType(Element element, String typeQualifiedName, IsValid valid) {
TypeMirror elementType = element.asType();
TypeElement typeElement = annotationHelper.typeElementFromQualifiedName(typeQualifiedName);
if (typeElement != null) {
TypeMirror expectedType = typeElement.asType();
if (!annotationHelper.isSubtype(elementType, expectedType)) {
valid.invalidate();
annotationHelper.printAnnotationError(element, "%s can only be used on an element that extends " + typeQualifiedName);
}
}
}
public void hasOneOrTwoParametersAndFirstIsBoolean(ExecutableElement executableElement, IsValid valid) {
List extends VariableElement> parameters = executableElement.getParameters();
if (parameters.size() < 1 || parameters.size() > 2) {
valid.invalidate();
annotationHelper.printAnnotationError(executableElement, "%s can only be used on a method with 1 or 2 parameter, instead of " + parameters.size());
} else {
VariableElement firstParameter = parameters.get(0);
TypeKind parameterKind = firstParameter.asType().getKind();
if (parameterKind != TypeKind.BOOLEAN && !firstParameter.toString().equals("java.lang.Boolean")) {
valid.invalidate();
annotationHelper.printAnnotationError(executableElement, "the first parameter should be a boolean");
}
}
}
public void allowedType(Element element, IsValid valid, TypeMirror fieldTypeMirror, List allowedTypes) {
String qualifiedName = fieldTypeMirror.toString();
if (!allowedTypes.contains(qualifiedName)) {
valid.invalidate();
annotationHelper.printAnnotationError(element, "%s can only be used on a field which is a " + allowedTypes.toString() + ", not " + qualifiedName);
}
}
public void hasRoboGuiceJars(Element element, IsValid valid) {
Elements elementUtils = annotationHelper.getElementUtils();
if (elementUtils.getTypeElement(CanonicalNameConstants.INJECTOR_PROVIDER) == null) {
valid.invalidate();
annotationHelper.printAnnotationError(element, "Could not find the RoboGuice framework in the classpath, the following class is missing: " + CanonicalNameConstants.INJECTOR_PROVIDER);
}
if (elementUtils.getTypeElement(RoboGuiceConstants.ROBOGUICE_APPLICATION_CLASS) == null) {
valid.invalidate();
annotationHelper.printAnnotationError(element, "Could not find the RoboApplication class in the classpath, are you using RoboGuice 1.1.1 ?");
}
try {
if (elementUtils.getTypeElement(CanonicalNameConstants.INJECTOR) == null) {
valid.invalidate();
annotationHelper.printAnnotationError(element, "Could not find the Guice framework in the classpath, the following class is missing: " + CanonicalNameConstants.INJECTOR);
}
} catch (RuntimeException e) {
valid.invalidate();
annotationHelper.printAnnotationError(element, "Could not find the Guice framework in the classpath, the following class is missing: " + CanonicalNameConstants.INJECTOR);
}
}
public void hasSpringAndroidJars(Element element, IsValid valid) {
Elements elementUtils = annotationHelper.getElementUtils();
if (elementUtils.getTypeElement(CanonicalNameConstants.REST_TEMPLATE) == null) {
valid.invalidate();
annotationHelper.printAnnotationError(element, "Could not find the SpringAndroid framework in the classpath, the following class is missing: " + CanonicalNameConstants.REST_TEMPLATE);
}
}
public void hasOrmLiteJars(Element element, IsValid valid) {
Elements elementUtils = annotationHelper.getElementUtils();
if (elementUtils.getTypeElement(CanonicalNameConstants.DAO) == null) {
valid.invalidate();
annotationHelper.printAnnotationError(element, "Could not find the OrmLite framework in the classpath, the following class is missing: " + CanonicalNameConstants.DAO);
}
}
public void androidService(AndroidSystemServices androidSystemServices, Element element, IsValid valid) {
TypeMirror serviceType = element.asType();
if (!androidSystemServices.contains(serviceType)) {
valid.invalidate();
annotationHelper.printAnnotationError(element, "Unknown service type: " + serviceType.toString());
}
}
public void hasOneMotionEventOrTwoMotionEventViewParameters(ExecutableElement executableElement, IsValid valid) {
List extends VariableElement> parameters = executableElement.getParameters();
if (parameters.size() < 1 || parameters.size() > 2) {
valid.invalidate();
annotationHelper.printAnnotationError(executableElement, "%s can only be used on a method with 1 (MotionEvent) or 2 (MotionEvent, View) parameters, instead of " + parameters.size());
} else {
VariableElement firstParameter = parameters.get(0);
String firstParameterType = firstParameter.asType().toString();
if (!firstParameterType.equals(CanonicalNameConstants.MOTION_EVENT)) {
valid.invalidate();
annotationHelper.printAnnotationError(executableElement, "the first parameter must be a " + CanonicalNameConstants.MOTION_EVENT + ", not a " + firstParameterType);
}
if (parameters.size() == 2) {
VariableElement secondParameter = parameters.get(1);
String secondParameterType = secondParameter.asType().toString();
if (!secondParameterType.equals(CanonicalNameConstants.VIEW)) {
valid.invalidate();
annotationHelper.printAnnotationError(executableElement, "the second parameter must be a " + CanonicalNameConstants.VIEW + ", not a " + secondParameterType);
}
}
}
}
public void hasOneOrTwoParametersAndFirstIsDb(ExecutableElement executableElement, IsValid valid) {
List extends VariableElement> parameters = executableElement.getParameters();
if (parameters.size() < 1) {
valid.invalidate();
annotationHelper.printAnnotationError(executableElement, "There should be at least 1 parameter: a " + CanonicalNameConstants.SQLITE_DATABASE);
} else {
VariableElement firstParameter = parameters.get(0);
String firstParameterType = firstParameter.asType().toString();
if (!firstParameterType.equals(CanonicalNameConstants.SQLITE_DATABASE)) {
valid.invalidate();
annotationHelper.printAnnotationError(executableElement, "the first parameter must be a " + CanonicalNameConstants.SQLITE_DATABASE + ", not a " + firstParameterType);
}
}
}
public void isDeclaredType(Element element, IsValid valid, TypeMirror uiFieldTypeMirror) {
if (!(uiFieldTypeMirror instanceof DeclaredType)) {
valid.invalidate();
annotationHelper.printAnnotationError(element, "%s can only be used on a field which is a declared type");
}
}
public boolean isPrefMethod(Element element) {
if (!element.getKind().equals(ElementKind.METHOD)) {
annotationHelper.printError(element, "Only methods are allowed in an " + annotationHelper.annotationName() + " annotated interface");
} else {
ExecutableElement executableElement = (ExecutableElement) element;
String methodName = executableElement.getSimpleName().toString();
if (executableElement.getParameters().size() > 0) {
annotationHelper.printError(element, "Method " + methodName + " should have no parameters in an " + annotationHelper.annotationName() + " annotated interface");
} else {
String returnType = executableElement.getReturnType().toString();
if (!VALID_PREF_RETURN_TYPES.contains(returnType)) {
annotationHelper.printError(element, "Method " + methodName + " should only return preference simple types in an " + annotationHelper.annotationName() + " annotated interface");
} else {
if (INVALID_PREF_METHOD_NAMES.contains(methodName)) {
annotationHelper.printError(element, "The method name " + methodName + " is forbidden in an " + annotationHelper.annotationName() + " annotated interface");
} else {
return true;
}
}
}
}
return false;
}
public void hasCorrectDefaultAnnotation(ExecutableElement method) {
checkDefaultAnnotation(method, DefaultBoolean.class, "boolean", new TypeKindAnnotationCondition(TypeKind.BOOLEAN));
checkDefaultAnnotation(method, DefaultFloat.class, "float", new TypeKindAnnotationCondition(TypeKind.FLOAT));
checkDefaultAnnotation(method, DefaultInt.class, "int", new TypeKindAnnotationCondition(TypeKind.INT));
checkDefaultAnnotation(method, DefaultLong.class, "long", new TypeKindAnnotationCondition(TypeKind.LONG));
checkDefaultAnnotation(method, DefaultString.class, "String", new DefaultAnnotationCondition() {
@Override
public boolean correctReturnType(TypeMirror returnType) {
return returnType.toString().equals(CanonicalNameConstants.STRING);
}
});
}
private interface DefaultAnnotationCondition {
boolean correctReturnType(TypeMirror returnType);
}
private class TypeKindAnnotationCondition implements DefaultAnnotationCondition {
private final TypeKind typeKind;
public TypeKindAnnotationCondition(TypeKind typeKind) {
this.typeKind = typeKind;
}
@Override
public boolean correctReturnType(TypeMirror returnType) {
return returnType.getKind() == typeKind;
}
}
private void checkDefaultAnnotation(ExecutableElement method, Class annotationClass, String expectedReturnType, DefaultAnnotationCondition condition) {
T defaultAnnotation = method.getAnnotation(annotationClass);
if (defaultAnnotation != null) {
if (!condition.correctReturnType(method.getReturnType())) {
annotationHelper.printAnnotationError(method, annotationClass, TargetAnnotationHelper.annotationName(annotationClass) + " can only be used on a method that returns a " + expectedReturnType);
}
}
}
@SuppressWarnings("unchecked")
private static final List> REST_ANNOTATION_CLASSES = Arrays.asList(Get.class, Head.class, Options.class, Post.class, Put.class, Delete.class);
public void unannotatedMethodReturnsRestTemplate(TypeElement typeElement, IsValid valid) {
List extends Element> enclosedElements = typeElement.getEnclosedElements();
boolean foundGetRestTemplateMethod = false;
boolean foundSetRestTemplateMethod = false;
boolean foundSetRootUrlMethod = false;
for (Element enclosedElement : enclosedElements) {
if (enclosedElement.getKind() != ElementKind.METHOD) {
valid.invalidate();
annotationHelper.printError(enclosedElement, "Only methods are allowed in a " + TargetAnnotationHelper.annotationName(Rest.class) + " annotated interface");
} else {
boolean hasRestAnnotation = false;
for (Class extends Annotation> annotationClass : REST_ANNOTATION_CLASSES) {
if (enclosedElement.getAnnotation(annotationClass) != null) {
hasRestAnnotation = true;
break;
}
}
if (!hasRestAnnotation) {
ExecutableElement executableElement = (ExecutableElement) enclosedElement;
TypeMirror returnType = executableElement.getReturnType();
if (returnType.toString().equals(CanonicalNameConstants.REST_TEMPLATE)) {
if (executableElement.getParameters().size() > 0) {
valid.invalidate();
annotationHelper.printError(enclosedElement, "The method returning a RestTemplate should not declare any parameter in a " + TargetAnnotationHelper.annotationName(Rest.class) + " annotated interface");
} else {
if (foundGetRestTemplateMethod) {
valid.invalidate();
annotationHelper.printError(enclosedElement, "Only one method should declare returning a RestTemplate in a " + TargetAnnotationHelper.annotationName(Rest.class) + " annotated interface");
} else {
foundGetRestTemplateMethod = true;
}
}
} else if (returnType.getKind() == TypeKind.VOID) {
List extends VariableElement> parameters = executableElement.getParameters();
if (parameters.size() == 1) {
VariableElement firstParameter = parameters.get(0);
if (firstParameter.asType().toString().equals(CanonicalNameConstants.REST_TEMPLATE)) {
if (!foundSetRestTemplateMethod) {
foundSetRestTemplateMethod = true;
} else {
valid.invalidate();
annotationHelper.printError(enclosedElement, "You can only have oneRestTemplate setter method on a " + TargetAnnotationHelper.annotationName(Rest.class) + " annotated interface");
}
} else if (executableElement.getSimpleName().toString().equals(METHOD_NAME_SET_ROOT_URL) && !foundSetRootUrlMethod) {
foundSetRootUrlMethod = true;
} else {
valid.invalidate();
annotationHelper.printError(enclosedElement, "The method to set a RestTemplate should have only one RestTemplate parameter on a " + TargetAnnotationHelper.annotationName(Rest.class) + " annotated interface");
}
} else {
valid.invalidate();
annotationHelper.printError(enclosedElement, "The method to set a RestTemplate should have only one RestTemplate parameter on a " + TargetAnnotationHelper.annotationName(Rest.class) + " annotated interface");
}
} else {
valid.invalidate();
annotationHelper.printError(enclosedElement, "All methods should be annotated in a " + TargetAnnotationHelper.annotationName(Rest.class) + " annotated interface, except the ones that returns or set a RestTemplate");
}
}
}
}
}
public void notAlreadyValidated(Element element, AnnotationElements validatedElements, IsValid valid) {
if (validatedElements.getAllElements().contains(element)) {
valid.invalidate();
annotationHelper.printAnnotationError(element, "%s annotated element cannot be used with the other annotations used on this element.");
}
}
public void hasEmptyOrContextConstructor(Element element, IsValid valid) {
List constructors = ElementFilter.constructorsIn(element.getEnclosedElements());
if (constructors.size() == 1) {
ExecutableElement constructor = constructors.get(0);
if (!annotationHelper.isPrivate(constructor)) {
if (constructor.getParameters().size() > 1) {
annotationHelper.printAnnotationError(element, "%s annotated element should have a constructor with one parameter max, of type " + CanonicalNameConstants.CONTEXT);
valid.invalidate();
} else if (constructor.getParameters().size() == 1) {
VariableElement parameter = constructor.getParameters().get(0);
if (!parameter.asType().toString().equals(CanonicalNameConstants.CONTEXT)) {
annotationHelper.printAnnotationError(element, "%s annotated element should have a constructor with one parameter max, of type " + CanonicalNameConstants.CONTEXT);
valid.invalidate();
}
}
} else {
annotationHelper.printAnnotationError(element, "%s annotated element should not have a private constructor");
valid.invalidate();
}
} else {
annotationHelper.printAnnotationError(element, "%s annotated element should have only one constructor");
valid.invalidate();
}
}
public void hasEmptyConstructor(Element element, IsValid valid) {
List constructors = ElementFilter.constructorsIn(element.getEnclosedElements());
if (constructors.size() == 1) {
ExecutableElement constructor = constructors.get(0);
if (!annotationHelper.isPrivate(constructor)) {
if (constructor.getParameters().size() != 0) {
annotationHelper.printAnnotationError(element, "%s annotated element should have an empty constructor");
valid.invalidate();
}
} else {
annotationHelper.printAnnotationError(element, "%s annotated element should not have a private constructor");
valid.invalidate();
}
} else {
annotationHelper.printAnnotationError(element, "%s annotated element should have only one constructor");
valid.invalidate();
}
}
public void hasValidLogLevel(Element element, IsValid isValid) {
Trace annotation = element.getAnnotation(Trace.class);
Integer level = annotation.level();
if (!VALID_LOG_LEVELS.contains(level)) {
annotationHelper.printError(element, "Unrecognized log level.");
isValid.invalidate();
}
}
public void canBeSavedAsInstanceState(Element element, IsValid isValid) {
String typeString = element.asType().toString();
if (!isKnowInstanceStateType(typeString)) {
if (element.asType() instanceof DeclaredType) {
DeclaredType declaredType = (DeclaredType) element.asType();
typeString = declaredType.asElement().toString();
} else if (element.asType() instanceof ArrayType) {
ArrayType arrayType = (ArrayType) element.asType();
TypeMirror componentType = arrayType.getComponentType();
if (componentType instanceof DeclaredType) {
DeclaredType declaredType = (DeclaredType) componentType;
typeString = declaredType.asElement().toString();
} else {
typeString = componentType.toString();
}
} else {
typeString = element.asType().toString();
}
TypeElement elementType = annotationHelper.typeElementFromQualifiedName(typeString);
if (elementType == null) {
elementType = getArrayEnclosingType(typeString);
if (elementType == null) {
annotationHelper.printAnnotationError(element, "Unrecognized type. Please let your attribute be primitive or implement Serializable or Parcelable");
isValid.invalidate();
}
}
if (elementType != null) {
TypeElement parcelableType = annotationHelper.typeElementFromQualifiedName("android.os.Parcelable");
TypeElement serializableType = annotationHelper.typeElementFromQualifiedName("java.io.Serializable");
if (!annotationHelper.isSubtype(elementType, parcelableType) && !annotationHelper.isSubtype(elementType, serializableType)) {
annotationHelper.printAnnotationError(element, "Unrecognized type. Please let your attribute be primitive or implement Serializable or Parcelable");
isValid.invalidate();
}
}
}
}
private TypeElement getArrayEnclosingType(String typeString) {
typeString = typeString.replace("[]", "");
return annotationHelper.typeElementFromQualifiedName(typeString);
}
private boolean isKnowInstanceStateType(String type) {
return InstanceStateProcessor.methodSuffixNameByTypeName.containsKey(type);
}
public void componentRegistered(Element element, AndroidManifest androidManifest, IsValid valid) {
componentRegistered(element, androidManifest, true, valid);
}
public void componentRegistered(Element element, AndroidManifest androidManifest, boolean printWarning, IsValid valid) {
TypeElement typeElement = (TypeElement) element;
if (typeElement.getModifiers().contains(Modifier.ABSTRACT)) {
return;
}
if (androidManifest.isLibraryProject()) {
return;
}
String componentQualifiedName = typeElement.getQualifiedName().toString();
String generatedComponentQualifiedName = componentQualifiedName + ModelConstants.GENERATION_SUFFIX;
List componentQualifiedNames = androidManifest.getComponentQualifiedNames();
if (!componentQualifiedNames.contains(generatedComponentQualifiedName)) {
String simpleName = typeElement.getSimpleName().toString();
String generatedSimpleName = simpleName + ModelConstants.GENERATION_SUFFIX;
if (componentQualifiedNames.contains(componentQualifiedName)) {
valid.invalidate();
annotationHelper.printAnnotationError(element, "The AndroidManifest.xml file contains the original component, and not the AndroidAnnotations generated component. Please register " + generatedSimpleName + " instead of " + simpleName);
} else {
if (printWarning) {
annotationHelper.printAnnotationWarning(element, "The component " + generatedSimpleName + " is not registered in the AndroidManifest.xml file.");
}
}
}
}
public void validateConverters(Element element, IsValid valid) {
TypeMirror httpMessageConverterType = annotationHelper.typeElementFromQualifiedName(HTTP_MESSAGE_CONVERTER).asType();
TypeMirror httpMessageConverterTypeErased = annotationHelper.getTypeUtils().erasure(httpMessageConverterType);
List converters = annotationHelper.extractAnnotationClassArrayParameter(element, annotationHelper.getTarget(), "converters");
for (DeclaredType converterType : converters) {
TypeMirror erasedConverterType = annotationHelper.getTypeUtils().erasure(converterType);
if (annotationHelper.isSubtype(erasedConverterType, httpMessageConverterTypeErased)) {
Element converterElement = converterType.asElement();
if (converterElement.getKind().isClass()) {
if (!annotationHelper.isAbstract(converterElement)) {
List constructors = ElementFilter.constructorsIn(converterElement.getEnclosedElements());
for (ExecutableElement constructor : constructors) {
if (annotationHelper.isPublic(constructor) && constructor.getParameters().isEmpty()) {
return;
}
}
valid.invalidate();
annotationHelper.printAnnotationError(element, "The converter class must have a public no argument constructor");
} else {
valid.invalidate();
annotationHelper.printAnnotationError(element, "The converter class must not be abstract");
}
} else {
valid.invalidate();
annotationHelper.printAnnotationError(element, "The converter class must be a class");
}
} else {
valid.invalidate();
annotationHelper.printAnnotationError(element, "The converter class must be a subtype of " + HTTP_MESSAGE_CONVERTER);
}
}
}
}