All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.androidannotations.helper.ValidatorHelper Maven / Gradle / Ivy

/**
 * Copyright (C) 2010-2016 eBusiness Information, Excilys Group
 * Copyright (C) 2016-2018 the AndroidAnnotations project
 *
 * 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 org.androidannotations.helper;

import static java.util.Arrays.asList;
import static org.androidannotations.helper.AndroidConstants.LOG_DEBUG;
import static org.androidannotations.helper.AndroidConstants.LOG_ERROR;
import static org.androidannotations.helper.AndroidConstants.LOG_INFO;
import static org.androidannotations.helper.AndroidConstants.LOG_VERBOSE;
import static org.androidannotations.helper.AndroidConstants.LOG_WARN;
import static org.androidannotations.helper.CanonicalNameConstants.INTERNET_PERMISSION;
import static org.androidannotations.helper.CanonicalNameConstants.WAKELOCK_PERMISSION;
import static org.androidannotations.helper.ModelConstants.VALID_ENHANCED_COMPONENT_ANNOTATIONS;
import static org.androidannotations.helper.ModelConstants.VALID_ENHANCED_VIEW_SUPPORT_ANNOTATIONS;
import static org.androidannotations.helper.ModelConstants.classSuffix;

import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;

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.TypeKind;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.ElementFilter;

import org.androidannotations.AndroidAnnotationsEnvironment;
import org.androidannotations.ElementValidation;
import org.androidannotations.annotations.EActivity;
import org.androidannotations.annotations.EBean;
import org.androidannotations.annotations.EFragment;
import org.androidannotations.annotations.EIntentService;
import org.androidannotations.annotations.EReceiver;
import org.androidannotations.annotations.EService;
import org.androidannotations.annotations.EView;
import org.androidannotations.annotations.EViewGroup;
import org.androidannotations.annotations.Trace;
import org.androidannotations.annotations.ViewById;
import org.androidannotations.internal.core.model.AndroidSystemServices;
import org.androidannotations.internal.model.AnnotationElements;

@SuppressWarnings("checkstyle:methodcount")
public class ValidatorHelper {

	private static final List ANDROID_FRAGMENT_QUALIFIED_NAMES = asList(CanonicalNameConstants.FRAGMENT, CanonicalNameConstants.SUPPORT_V4_FRAGMENT, CanonicalNameConstants.ANDROIDX_FRAGMENT);

	private static final Collection VALID_LOG_LEVELS = asList(LOG_VERBOSE, LOG_DEBUG, LOG_INFO, LOG_WARN, LOG_ERROR);

	private static final List VALID_PREFERENCE_CLASSES = asList(CanonicalNameConstants.PREFERENCE_ACTIVITY, CanonicalNameConstants.PREFERENCE_FRAGMENT,
			CanonicalNameConstants.SUPPORT_V4_PREFERENCE_FRAGMENT, CanonicalNameConstants.MACHINARIUS_V4_PREFERENCE_FRAGMENT, CanonicalNameConstants.SUPPORT_V7_PREFERENCE_FRAGMENTCOMPAT,
			CanonicalNameConstants.SUPPORT_V14_PREFERENCE_FRAGMENT, CanonicalNameConstants.ANDROIDX_PREFERENCE_FRAGMENT, CanonicalNameConstants.ANDROIDX_PREFERENCE_FRAGMENTCOMPAT);

	protected final TargetAnnotationHelper annotationHelper;
	private final ParcelerHelper parcelerHelper;

	public final ValidatorParameterHelper param;

	public ValidatorHelper(TargetAnnotationHelper targetAnnotationHelper) {
		annotationHelper = targetAnnotationHelper;
		param = new ValidatorParameterHelper(annotationHelper);
		parcelerHelper = new ParcelerHelper(environment());
	}

	protected AndroidAnnotationsEnvironment environment() {
		return annotationHelper.getEnvironment();
	}

	protected AnnotationElements validatedModel() {
		return environment().getValidatedElements();
	}

	public void isNotFinal(Element element, ElementValidation valid) {
		if (annotationHelper.isFinal(element)) {
			valid.addError("%s cannot be used on a final element");
		}
	}

	public void isNotSynchronized(Element element, ElementValidation valid) {
		if (annotationHelper.isSynchronized(element)) {
			valid.addError("%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, ElementValidation valid) {
		if (!annotationHelper.isInterface(element)) {
			valid.addError("%s can only be used on an interface");
		}
	}

	public void isNotInterface(TypeElement element, ElementValidation valid) {
		if (annotationHelper.isInterface(element)) {
			valid.addError("%s cannot be used on an interface");
		}
	}

	public void isTopLevel(TypeElement element, ElementValidation valid) {
		if (!annotationHelper.isTopLevel(element)) {
			valid.addError("%s can only be used on a top level type");
		}
	}

	public void doesNotReturnPrimitive(ExecutableElement element, ElementValidation valid) {
		if (element.getReturnType().getKind().isPrimitive()) {
			valid.addError("%s cannot return primitive");
		}
	}

	public void isNotPrivate(Element element, ElementValidation valid) {
		if (annotationHelper.isPrivate(element)) {
			valid.addError("%s cannot be used on a private element");
		}
	}

	public void isPublic(Element element, ElementValidation valid) {
		if (!annotationHelper.isPublic(element)) {
			valid.addError(element, "%s cannot be used on a non public element");
		}
	}

	public void isStatic(Element element, ElementValidation valid) {
		if (!annotationHelper.isStatic(element)) {
			valid.addError(element, "%s cannot be used on a non static inner element");
		}
	}

	public void enclosingElementIsNotAbstractIfNotAbstract(Element element, ElementValidation validation) {
		if (!annotationHelper.isAbstract(element) && annotationHelper.isAbstract(element.getEnclosingElement())) {
			validation.addError("%s cannot be used on a non-abstract inner element whose outer element is abstract");
		}
	}

	public void enclosingElementHasAnnotation(Class annotation, Element element, ElementValidation validation) {
		enclosingElementHasOneOfAnnotations(element, Collections.> singletonList(annotation), validation);
	}

	public void enclosingElementHasEBeanAnnotation(Element element, ElementValidation valid) {
		enclosingElementHasAnnotation(EBean.class, element, valid);
	}

	public void enclosingElementHasEActivity(Element element, ElementValidation valid) {
		enclosingElementHasAnnotation(EActivity.class, element, valid);
	}

	public void enclosingElementHasEActivityOrEFragment(Element element, ElementValidation valid) {
		List> validAnnotations = asList(EActivity.class, EFragment.class);
		enclosingElementHasOneOfAnnotations(element, validAnnotations, valid);
	}

	public void enclosingElementHasEActivityOrEFragmentOrEViewOrEViewGroup(Element element, ElementValidation valid) {
		List> validAnnotations = asList(EActivity.class, EFragment.class, EView.class, EViewGroup.class);
		enclosingElementHasOneOfAnnotations(element, validAnnotations, valid);
	}

	public void enclosingElementHasEActivityOrEFragmentOrEServiceOrEIntentServiceOrEViewOrEViewGroup(Element element, ElementValidation valid) {
		List> validAnnotations = asList(EActivity.class, EFragment.class, EService.class, EIntentService.class, EView.class, EViewGroup.class);
		enclosingElementHasOneOfAnnotations(element, validAnnotations, valid);
	}

	public void enclosingElementHasEFragment(Element element, ElementValidation valid) {
		enclosingElementHasAnnotation(EFragment.class, element, valid);
	}

	public void enclosingElementHasEIntentService(Element element, ElementValidation valid) {
		enclosingElementHasAnnotation(EIntentService.class, element, valid);
	}

	public void enclosingElementHasEReceiver(Element element, ElementValidation valid) {
		enclosingElementHasAnnotation(EReceiver.class, element, valid);
	}

	public void hasEActivity(Element element, ElementValidation valid) {
		hasAnnotation(element, element, EActivity.class, valid);
	}

	public void hasEActivityOrEFragment(Element element, ElementValidation valid) {
		List> validAnnotations = asList(EActivity.class, EFragment.class);
		hasOneOfAnnotations(element, element, validAnnotations, valid);
	}

	public void enclosingElementHasEnhancedViewSupportAnnotation(Element element, ElementValidation valid) {
		enclosingElementHasOneOfAnnotations(element, VALID_ENHANCED_VIEW_SUPPORT_ANNOTATIONS, valid);
	}

	public void enclosingElementHasEnhancedComponentAnnotation(Element element, ElementValidation valid) {
		enclosingElementHasOneOfAnnotations(element, VALID_ENHANCED_COMPONENT_ANNOTATIONS, valid);
	}

	public void enclosingElementHasAndroidAnnotation(Element element, ElementValidation valid) {
		enclosingElementHasOneOfAnnotations(element, environment().getGeneratingAnnotations(), valid);
	}

	private void hasAnnotation(Element element, Element reportElement, Class validAnnotation, ElementValidation valid) {
		ArrayList> validAnnotations = new ArrayList<>();
		validAnnotations.add(validAnnotation);
		hasOneOfAnnotations(element, reportElement, validAnnotations, valid);
	}

	public void enclosingElementHasOneOfAnnotations(Element element, List> validAnnotations, ElementValidation validation) {
		hasOneOfAnnotations(element, element.getEnclosingElement(), validAnnotations, validation);
	}

	public void hasOneOfAnnotations(Element reportElement, Element element, List> validAnnotations, ElementValidation validation) {
		checkAnnotations(reportElement, element, validAnnotations, true, validation);
	}

	public void doesNotHaveOneOfAnnotations(Element element, List> validAnnotations, ElementValidation validation) {
		checkAnnotations(element, element, validAnnotations, false, validation);
	}

	public void doesNotHaveAnnotation(Element element, Class annotation, ElementValidation validation) {
		doesNotHaveOneOfAnnotations(element, Collections.> singletonList(annotation), validation);
	}

	public void doesNotHaveAnyOfSupportedAnnotations(Element element, ElementValidation validation) {
		Set supportedAnnotationTypes = environment().getSupportedAnnotationTypes();
		for (AnnotationMirror annotationMirror : element.getAnnotationMirrors()) {
			if (supportedAnnotationTypes.contains(annotationMirror.getAnnotationType().toString())) {
				validation.addError(element, "method injection does only allow the annotation to be placed on the method OR on each parameter.");
				break;
			}
		}
	}

	private void checkAnnotations(Element reportElement, Element element, List> validAnnotations, boolean shouldFind, ElementValidation validation) {

		boolean foundAnnotation = false;
		for (Class validAnnotation : validAnnotations) {
			if (element.getAnnotation(validAnnotation) != null) {
				foundAnnotation = true;
				break;
			}
		}

		if (shouldFind != foundAnnotation) {
			String not = shouldFind ? "" : " not";

			validation.addError(reportElement,
					"%s can only be used in a " + element.getKind().toString().toLowerCase() + not + " annotated with " + getFormattedValidEnhancedBeanAnnotationTypes(validAnnotations) + ".");
		}
	}

	private String getFormattedValidEnhancedBeanAnnotationTypes(List> annotations) {
		StringBuilder sb = new StringBuilder();
		if (!annotations.isEmpty()) {
			sb.append("@" + annotations.get(0).getName());

			for (int i = 1; i < annotations.size(); i++) {
				sb.append(", ");
				sb.append("@" + annotations.get(i));
			}
		}

		return sb.toString();
	}

	public void hasViewByIdAnnotation(Element element, ElementValidation valid) {
		String error = "can only be used with annotation";
		elementHasAnnotation(ViewById.class, element, valid, error);
	}

	public void enclosingElementHasAnnotation(Class annotation, Element element, ElementValidation valid, String error) {
		Element enclosingElement = element.getEnclosingElement();
		elementHasAnnotation(annotation, enclosingElement, valid, error);
	}

	public void elementHasAnnotation(Class annotation, Element element, ElementValidation valid, String error) {
		if (!elementHasAnnotation(annotation, element)) {
			if (element.getAnnotation(annotation) == null) {
				valid.addError("%s " + error + " @" + annotation.getName());
			}
		}
	}

	public boolean elementHasAnnotation(Class annotation, Element element) {
		Set layoutAnnotatedElements = validatedModel().getRootAnnotatedElements(annotation.getName());
		return layoutAnnotatedElements.contains(element);
	}

	public void typeHasAnnotation(Class annotation, Element element, ElementValidation valid) {
		TypeMirror elementType = element.asType();
		typeHasAnnotation(annotation, elementType, valid);
	}

	public void typeHasValidAnnotation(Class annotation, Element element, ElementValidation valid) {
		typeHasAnnotation(annotation, element, valid);

		if (valid.isValid()) {
			typeIsValid(annotation, element.asType(), valid);
		}
	}

	public void typeHasAnnotation(Class annotation, TypeMirror elementType, ElementValidation valid) {
		Element typeElement = annotationHelper.getTypeUtils().asElement(elementType);
		if (!elementHasAnnotationSafe(annotation, typeElement)) {
			valid.addError("%s can only be used on an element annotated with @" + annotation.getName());
		}
	}

	public void typeOrTargetValueHasAnnotation(Class annotation, Element element, ElementValidation valid) {
		Element targetElement = findTargetElement(element, valid);
		if (targetElement == null) {
			return;
		}

		DeclaredType targetAnnotationClassValue = annotationHelper.extractAnnotationClassParameter(element);

		if (targetAnnotationClassValue != null) {
			targetElement = targetAnnotationClassValue.asElement();

			if (!annotationHelper.getTypeUtils().isAssignable(targetAnnotationClassValue, targetElement.asType())) {
				valid.addError("The value of %s must be assignable into the annotated field");
			}
		}

		typeHasValidAnnotation(annotation, targetElement, valid);
	}

	Element findTargetElement(Element element, ElementValidation valid) {
		if (element instanceof ExecutableElement) {
			ExecutableElement executableElement = (ExecutableElement) element;
			returnTypeIsVoid(executableElement, valid);
			if (!valid.isValid()) {
				return null;
			}

			List parameters = executableElement.getParameters();
			if (parameters.size() != 1) {
				valid.addError("The method can only have 1 parameter");
				return null;
			}
			return parameters.get(0);
		}
		return element;
	}

	public void typeIsValid(Class annotation, TypeMirror elementType, ElementValidation elementValidation) {
		Set validElements = validatedModel().getRootAnnotatedElements(annotation.getName());

		Set extractedElements = environment().getExtractedElements().getRootAnnotatedElements(annotation.getName());

		Element typeElement = annotationHelper.getTypeUtils().asElement(elementType);

		if (!extractedElements.contains(typeElement) || validElements.contains(typeElement)) {
			return;
		}

		elementValidation.addError("The type " + typeElement.getSimpleName() + " is invalid, " + "please check the messages on that type.");
	}

	private boolean elementHasAnnotationSafe(Class annotation, Element element) {
		List annotationMirrors = element.getAnnotationMirrors();
		for (AnnotationMirror annotationMirror : annotationMirrors) {
			if (annotationMirror.getAnnotationType().toString().equals(annotation.getName())) {
				return true;
			}
		}
		return false;
	}

	public int numberOfElementParameterHasAnnotation(ExecutableElement element, Class annotation) {
		int count = 0;
		for (VariableElement parameter : element.getParameters()) {
			if (parameter.getAnnotation(annotation) != null) {
				count++;
			}
		}
		return count;
	}

	public void doesntThrowException(Element element, ElementValidation valid) {
		ExecutableElement executableElement = (ExecutableElement) element;
		if (executableElement.getThrownTypes().size() > 0) {
			valid.addError("%s annotated methods should not declare throwing any exception");
		}
	}

	public void returnTypeIsVoidOrBoolean(ExecutableElement executableElement, ElementValidation valid) {
		TypeMirror returnType = executableElement.getReturnType();
		TypeKind returnKind = returnType.getKind();
		if (returnKind != TypeKind.BOOLEAN && returnKind != TypeKind.VOID && !returnType.toString().equals(CanonicalNameConstants.BOOLEAN)) {
			valid.addError("%s can only be used on a method with a boolean or a void return type");
		}
	}

	public void returnTypeIsVoid(ExecutableElement executableElement, ElementValidation valid) {
		TypeMirror returnType = executableElement.getReturnType();
		if (returnType.getKind() != TypeKind.VOID) {
			valid.addError("%s can only be used on a method with a void return type");
		}
	}

	public void returnTypeIsNotVoid(ExecutableElement executableElement, ElementValidation valid) {
		TypeMirror returnType = executableElement.getReturnType();
		if (returnType.getKind() == TypeKind.VOID) {
			valid.addError("%s can only be used on a method with a return type non void");
		}
	}

	public void extendsActivity(Element element, ElementValidation valid) {
		extendsType(element, CanonicalNameConstants.ACTIVITY, valid);
	}

	public void extendsFragment(Element element, ElementValidation valid) {
		extendsOneOfTypes(element, ANDROID_FRAGMENT_QUALIFIED_NAMES, valid);
	}

	public void extendsService(Element element, ElementValidation valid) {
		extendsType(element, CanonicalNameConstants.SERVICE, valid);
	}

	public void extendsIntentService(Element element, ElementValidation valid) {
		extendsType(element, CanonicalNameConstants.INTENT_SERVICE, valid);
	}

	public void extendsReceiver(Element element, ElementValidation valid) {
		extendsType(element, CanonicalNameConstants.BROADCAST_RECEIVER, valid);
	}

	public void extendsProvider(Element element, ElementValidation valid) {
		extendsType(element, CanonicalNameConstants.CONTENT_PROVIDER, valid);
	}

	public void extendsView(Element element, ElementValidation valid) {
		extendsType(element, CanonicalNameConstants.VIEW, valid);
	}

	public void extendsTextView(Element element, ElementValidation valid) {
		extendsType(element, CanonicalNameConstants.TEXT_VIEW, valid);
	}

	public void extendsViewGroup(Element element, ElementValidation valid) {
		extendsType(element, CanonicalNameConstants.VIEW_GROUP, valid);
	}

	public void extendsApplication(Element element, ElementValidation valid) {
		extendsType(element, CanonicalNameConstants.APPLICATION, valid);
	}

	public void extendsContext(Element element, ElementValidation valid) {
		extendsType(element, CanonicalNameConstants.CONTEXT, valid);
	}

	public void extendsMenuItem(Element element, ElementValidation valid) {
		Element enclosingElement = element.getEnclosingElement();
		String enclosingQualifiedName = enclosingElement.asType().toString();
		TypeElement enclosingTypeElement = annotationHelper.typeElementFromQualifiedName(enclosingQualifiedName);

		if (enclosingTypeElement != null) {
			extendsType(element, CanonicalNameConstants.MENU_ITEM, valid);
		}
	}

	public void extendsMenu(Element element, ElementValidation validation) {
		Element enclosingElement = element.getEnclosingElement();
		String enclosingQualifiedName = enclosingElement.asType().toString();
		TypeElement enclosingTypeElement = annotationHelper.typeElementFromQualifiedName(enclosingQualifiedName);

		if (enclosingTypeElement != null) {
			extendsType(element, CanonicalNameConstants.MENU, validation);
		}
	}

	public void extendsListOfView(Element element, ElementValidation valid) {
		DeclaredType elementType = (DeclaredType) element.asType();
		List elementTypeArguments = elementType.getTypeArguments();

		TypeMirror viewType = annotationHelper.typeElementFromQualifiedName(CanonicalNameConstants.VIEW).asType();

		if (!elementType.toString().equals(CanonicalNameConstants.LIST) && elementTypeArguments.size() == 1 && !annotationHelper.isSubtype(elementTypeArguments.get(0), viewType)) {
			valid.invalidate();
			valid.addError("%s can only be used on a " + CanonicalNameConstants.LIST + " of elements extending " + CanonicalNameConstants.VIEW);
		}
	}

	public void extendsPreference(Element element, ElementValidation validation) {
		extendsOneOfTypes(element, asList(CanonicalNameConstants.PREFERENCE, CanonicalNameConstants.SUPPORT_V7_PREFERENCE, CanonicalNameConstants.ANDROIDX_PREFERENCE), validation);
	}

	public void extendsOneOfTypes(Element element, List typeQualifiedNames, ElementValidation 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.addError("%s can only be used on an element that extends one of the following classes: " + typeQualifiedNames);
	}

	public void extendsType(Element element, String typeQualifiedName, ElementValidation valid) {
		if (!extendsType(element, typeQualifiedName)) {
			valid.addError("%s can only be used on an element that extends " + typeQualifiedName);
		}
	}

	protected boolean extendsType(Element element, String typeQualifiedName) {
		TypeMirror elementType = element.asType();

		TypeElement typeElement = annotationHelper.typeElementFromQualifiedName(typeQualifiedName);
		if (typeElement != null) {
			TypeMirror expectedType = typeElement.asType();
			return annotationHelper.isSubtype(elementType, expectedType);
		}
		return false;
	}

	public void allowedType(Element element, List allowedTypes, ElementValidation valid) {
		String qualifiedName;
		Element enclosingElement = element.getEnclosingElement();
		if (element instanceof VariableElement && enclosingElement instanceof ExecutableElement) {
			qualifiedName = element.asType().toString();
		} else if (element instanceof ExecutableElement) {
			element = ((ExecutableElement) element).getParameters().get(0);
			qualifiedName = element.asType().toString();
		} else {
			qualifiedName = element.asType().toString();
		}

		if (!allowedTypes.contains(qualifiedName)) {
			valid.addError("%s can only be used on a field which is a " + allowedTypes.toString() + ", not " + qualifiedName);
		}
	}

	public void androidService(Element element, ElementValidation valid) {
		Element targetElement = findTargetElement(element, valid);
		if (targetElement == null) {
			return;
		}

		AndroidSystemServices androidSystemServices = new AndroidSystemServices(environment());
		TypeMirror serviceType = targetElement.asType();
		if (!androidSystemServices.contains(serviceType)) {
			valid.addError("Unknown service type: " + serviceType.toString());
		}
	}

	public void isDeclaredType(Element element, ElementValidation valid) {
		if (!(element.asType() instanceof DeclaredType)) {
			valid.addError("%s can only be used on a field which is a declared type");
		}
	}

	public void notAlreadyValidated(Element element, ElementValidation valid) {
		if (validatedModel().getAllElements().contains(element)) {
			valid.addError("%s annotated element cannot be used with the other annotations used on this element.");
		}
	}

	public void isAbstractOrHasEmptyOrContextConstructor(Element element, ElementValidation valid) {
		List constructors = ElementFilter.constructorsIn(element.getEnclosedElements());

		if (!annotationHelper.isAbstract(element)) {
			if (constructors.size() == 1) {
				ExecutableElement constructor = constructors.get(0);

				if (!annotationHelper.isPrivate(constructor)) {
					if (constructor.getParameters().size() > 1) {
						valid.addError("%s annotated element should have a constructor with one parameter max, of type " + CanonicalNameConstants.CONTEXT);
					} else if (constructor.getParameters().size() == 1) {
						VariableElement parameter = constructor.getParameters().get(0);
						if (!parameter.asType().toString().equals(CanonicalNameConstants.CONTEXT)) {
							valid.addError("%s annotated element should have a constructor with one parameter max, of type " + CanonicalNameConstants.CONTEXT);
						}
					}
				} else {
					valid.addError("%s annotated element should not have a private constructor");
				}
			} else {
				valid.addError("%s annotated element should have only one constructor");
			}
		}
	}

	public void isAbstractOrHasEmptyConstructor(Element element, ElementValidation valid) {
		List constructors = ElementFilter.constructorsIn(element.getEnclosedElements());

		if (!annotationHelper.isAbstract(element)) {
			if (constructors.size() == 1) {
				ExecutableElement constructor = constructors.get(0);

				if (!annotationHelper.isPrivate(constructor)) {
					if (constructor.getParameters().size() != 0) {
						valid.addError("%s annotated element should have an empty constructor");
					}
				} else {
					valid.addError("%s annotated element should not have a private constructor");
				}
			} else {
				valid.addError("%s annotated element should have only one constructor");
			}
		}
	}

	public void hasValidLogLevel(Element element, ElementValidation valid) {

		Trace annotation = element.getAnnotation(Trace.class);
		Integer level = annotation.level();

		if (!VALID_LOG_LEVELS.contains(level)) {
			valid.addError("Unrecognized log level.");
		}

	}

	public void canBePutInABundle(Element element, ElementValidation valid) {
		TypeMirror typeMirror = element.asType();
		String typeString = element.asType().toString();

		if (!isKnownBundleCompatibleType(typeString)) {

			if (typeMirror instanceof ArrayType) {
				ArrayType arrayType = (ArrayType) element.asType();
				typeMirror = arrayType.getComponentType();
			}

			if (typeMirror.getKind() != TypeKind.NONE) {
				TypeMirror parcelableType = annotationHelper.typeElementFromQualifiedName(CanonicalNameConstants.PARCELABLE).asType();
				TypeMirror serializableType = annotationHelper.typeElementFromQualifiedName("java.io.Serializable").asType();

				if (typeString.startsWith(CanonicalNameConstants.SPARSE_ARRAY)) {
					DeclaredType declaredType = (DeclaredType) typeMirror;
					List typeArguments = declaredType.getTypeArguments();
					if (typeArguments.size() != 1 || !annotationHelper.isSubtype(typeArguments.get(0), parcelableType)) {
						valid.addError("Unrecognized type. The type argument of SparseArray should implement Parcelable.");
					}
				} else if (!annotationHelper.isSubtype(typeMirror, parcelableType) && !annotationHelper.isSubtype(typeMirror, serializableType) && !parcelerHelper.isParcelType(typeMirror)) {
					valid.addError("Unrecognized type. Please let your attribute be primitive or implement Serializable or Parcelable or an annotated Parceler bean.");
				}
			}
		}
	}

	private boolean isKnownBundleCompatibleType(String type) {
		return BundleHelper.METHOD_SUFFIX_BY_TYPE_NAME.containsKey(type);
	}

	public void componentRegistered(Element element, AndroidManifest androidManifest, ElementValidation valid) {
		componentRegistered(element, androidManifest, true, valid);
	}

	public void componentRegistered(Element element, AndroidManifest androidManifest, boolean printWarning, ElementValidation valid) {
		TypeElement typeElement = (TypeElement) element;

		if (typeElement.getModifiers().contains(Modifier.ABSTRACT)) {
			return;
		}

		if (androidManifest.isLibraryProject()) {
			return;
		}

		String componentQualifiedName = typeElement.getQualifiedName().toString();
		String generatedComponentQualifiedName = componentQualifiedName + classSuffix();

		List componentQualifiedNames = androidManifest.getComponentQualifiedNames();
		if (!componentQualifiedNames.contains(generatedComponentQualifiedName)) {
			String simpleName = typeElement.getSimpleName().toString();
			String generatedSimpleName = simpleName + classSuffix();
			if (componentQualifiedNames.contains(componentQualifiedName)) {
				valid.addError("The AndroidManifest.xml file contains the original component, and not the AndroidAnnotations generated component. Please register " + generatedSimpleName
						+ " instead of " + simpleName);
			} else {
				if (printWarning) {
					valid.addWarning("The component " + generatedSimpleName + " is not registered in the AndroidManifest.xml file.");
				}
			}
		}

	}

	public void isDebuggable(AndroidManifest androidManifest, ElementValidation valid) {
		if (!androidManifest.isDebuggable()) {
			valid.addError("The application must be in debuggable mode. Please set android:debuggable to true in your AndroidManifest.xml file.");
		}
	}

	public void hasInternetPermission(AndroidManifest androidManifest, ElementValidation valid) {
		hasPermission(androidManifest, valid, INTERNET_PERMISSION);
	}

	public void hasWakeLockPermission(AndroidManifest androidManifest, ElementValidation valid) {
		hasPermission(androidManifest, valid, WAKELOCK_PERMISSION);
	}

	public void hasPermission(AndroidManifest androidManifest, ElementValidation valid, String permissionQualifiedName) {
		List permissionQualifiedNames = androidManifest.getPermissionQualifiedNames();
		if (!permissionQualifiedNames.contains(permissionQualifiedName)) {
			if (androidManifest.isLibraryProject()) {
				valid.addWarning("Your library should require the " + permissionQualifiedName + " permission.");
			} else {
				valid.addError("Your application must require the " + permissionQualifiedName + " permission.");
			}
		}
	}

	public void hasNotMultipleAnnotatedMethodWithSameName(Element element, ElementValidation valid, Class annotation) {
		Set actionNames = new TreeSet<>();

		List enclosedElements = element.getEnclosedElements();
		for (Element enclosedElement : enclosedElements) {
			if (enclosedElement.getKind() != ElementKind.METHOD || !annotationHelper.hasOneOfClassAnnotations(enclosedElement, annotation)) {
				continue;
			}

			String enclosedElementName = enclosedElement.getSimpleName().toString();
			if (actionNames.contains(enclosedElementName)) {
				valid.addError(enclosedElement, "The " + TargetAnnotationHelper.annotationName(annotation) + " annotated method must have unique name even if the signature is not the same");
			} else {
				actionNames.add(enclosedElementName);
			}
		}
	}

	public void extendsPreferenceActivityOrPreferenceFragment(Element element, ElementValidation valid) {
		extendsOneOfTypes(element, VALID_PREFERENCE_CLASSES, valid);
	}

	public void extendsPreferenceActivity(Element element, ElementValidation valid) {
		extendsType(element, CanonicalNameConstants.PREFERENCE_ACTIVITY, valid);
	}

	public void enclosingElementExtendsPreferenceActivityOrPreferenceFragment(Element element, ElementValidation valid) {
		extendsOneOfTypes(element.getEnclosingElement(), VALID_PREFERENCE_CLASSES, valid);
	}

	public boolean isClassPresent(String className) {
		return annotationHelper.getElementUtils().getTypeElement(className) != null;
	}

	public void isPreferenceFragmentClassPresent(Element element, ElementValidation valid) {
		if (!isClassPresent(CanonicalNameConstants.PREFERENCE_FRAGMENT)) {
			valid.addError("The class " + CanonicalNameConstants.PREFERENCE_FRAGMENT + " cannot be found. You have to use at least API 11");
		}
	}

	public void isViewPagerClassPresent(ElementValidation validation) {
		if (!isClassPresent(CanonicalNameConstants.VIEW_PAGER) && !isClassPresent(CanonicalNameConstants.ANDROIDX_VIEW_PAGER)) {
			validation.addError("The classes " + CanonicalNameConstants.VIEW_PAGER + " and " + CanonicalNameConstants.ANDROIDX_VIEW_PAGER
					+ " cannot be found. You have to include support-v4 or androidx.viewpager library");
		}
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy