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

org.androidannotations.internal.process.ModelProcessor Maven / Gradle / Ivy

There is a newer version: 4.8.0
Show newest version
/**
 * Copyright (C) 2010-2016 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 org.androidannotations.internal.process;

import java.util.Set;

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.NestingKind;
import javax.lang.model.element.TypeElement;

import org.androidannotations.handler.AnnotationHandler;
import org.androidannotations.handler.GeneratingAnnotationHandler;
import org.androidannotations.holder.GeneratedClassHolder;
import org.androidannotations.internal.InternalAndroidAnnotationsEnvironment;
import org.androidannotations.internal.exception.ProcessingException;
import org.androidannotations.internal.model.AnnotationElements;
import org.androidannotations.internal.model.AnnotationElements.AnnotatedAndRootElements;
import org.androidannotations.logger.Logger;
import org.androidannotations.logger.LoggerFactory;

import com.helger.jcodemodel.JCodeModel;

public class ModelProcessor {

	private static final Logger LOGGER = LoggerFactory.getLogger(ModelProcessor.class);

	public static class ProcessResult {

		public final JCodeModel codeModel;
		public final OriginatingElements originatingElements;

		public ProcessResult(JCodeModel codeModel, OriginatingElements originatingElements) {

			this.codeModel = codeModel;
			this.originatingElements = originatingElements;
		}
	}

	private final InternalAndroidAnnotationsEnvironment environment;

	public ModelProcessor(InternalAndroidAnnotationsEnvironment environment) {
		this.environment = environment;
	}

	@SuppressWarnings({ "unchecked", "rawtypes" })
	public ProcessResult process(AnnotationElements validatedModel) throws Exception {
		ProcessHolder processHolder = new ProcessHolder(environment.getProcessingEnvironment());

		environment.setProcessHolder(processHolder);

		LOGGER.info("Processing root elements");

		/*
		 * We generate top classes then inner classes, then inner classes of
		 * inner classes, etc... until there is no more classes to generate.
		 */
		while (generateElements(validatedModel, processHolder)) {
			// CHECKSTYLE:OFF
			;
			// CHECKSTYLE:ON
		}

		LOGGER.info("Processing enclosed elements");

		for (AnnotationHandler annotationHandler : environment.getDecoratingHandlers()) {
			if (!annotationHandler.isEnabled()) {
				continue;
			}
			String annotationName = annotationHandler.getTarget();

			/*
			 * For ancestors, the annotationHandler manipulates the annotated
			 * elements, but uses the holder for the root element
			 */
			Set ancestorAnnotatedElements = validatedModel.getAncestorAnnotatedElements(annotationName);

			if (!ancestorAnnotatedElements.isEmpty()) {
				LOGGER.debug("Processing enclosed elements with {}: {}", annotationHandler.getClass().getSimpleName(), ancestorAnnotatedElements);
			}

			for (AnnotatedAndRootElements elements : ancestorAnnotatedElements) {
				GeneratedClassHolder holder = processHolder.getGeneratedClassHolder(elements.rootTypeElement);
				/*
				 * Annotations coming from ancestors may be applied to root
				 * elements that are not validated, and therefore not available.
				 */
				if (holder != null) {
					processThrowing(annotationHandler, elements.annotatedElement, holder);
				}
			}

			Set rootAnnotatedElements = validatedModel.getRootAnnotatedElements(annotationName);

			for (Element annotatedElement : rootAnnotatedElements) {

				Element enclosingElement;
				if (annotatedElement instanceof TypeElement) {
					enclosingElement = annotatedElement;
				} else {
					enclosingElement = annotatedElement.getEnclosingElement();
					/*
					 * we are processing a method parameter
					 */
					if (enclosingElement instanceof ExecutableElement) {
						enclosingElement = enclosingElement.getEnclosingElement();
					}
				}

				/*
				 * We do not generate code for elements belonging to abstract
				 * classes, because the generated classes are final anyway
				 */
				if (!isAbstractClass(enclosingElement)) {
					GeneratedClassHolder holder = processHolder.getGeneratedClassHolder(enclosingElement);
					
					/*
					 * The holder can be null if the annotated holder class is
					 * already invalidated.
					 */
					if (holder != null) {
						processThrowing(annotationHandler, annotatedElement, holder);
					}
				} else {
					LOGGER.trace("Skip element {} because enclosing element {} is abstract", annotatedElement, enclosingElement);
				}
			}

		}

		return new ProcessResult(//
				processHolder.codeModel(), //
				processHolder.getOriginatingElements());
	}

	private  void processThrowing(AnnotationHandler handler, Element element, T generatedClassHolder) throws ProcessingException {
		try {
			handler.process(element, generatedClassHolder);
		} catch (Exception e) {
			throw new ProcessingException(e, element);
		}
	}

	private boolean isAbstractClass(Element annotatedElement) {
		if (annotatedElement instanceof TypeElement) {
			TypeElement typeElement = (TypeElement) annotatedElement;

			return typeElement.getKind() == ElementKind.CLASS && typeElement.getModifiers().contains(Modifier.ABSTRACT);
		} else {
			return false;
		}
	}

	@SuppressWarnings({ "unchecked", "rawtypes" })
	private boolean generateElements(AnnotationElements validatedModel, ProcessHolder processHolder) throws Exception {
		boolean isElementRemaining = false;
		for (GeneratingAnnotationHandler generatingAnnotationHandler : environment.getGeneratingHandlers()) {
			if (!generatingAnnotationHandler.isEnabled()) {
				continue;
			}
			String annotationName = generatingAnnotationHandler.getTarget();
			Set annotatedElements = validatedModel.getRootAnnotatedElements(annotationName);

			if (!annotatedElements.isEmpty()) {
				LOGGER.debug("Processing root elements {}: {}", generatingAnnotationHandler.getClass().getSimpleName(), annotatedElements);
			}

			for (Element annotatedElement : annotatedElements) {
				/*
				 * We do not generate code for abstract classes, because the
				 * generated classes are final anyway (we do not want anyone to
				 * extend them).
				 */
				if (!isAbstractClass(annotatedElement)) {
					if (processHolder.getGeneratedClassHolder(annotatedElement) == null) {
						TypeElement typeElement = (TypeElement) annotatedElement;
						Element enclosingElement = annotatedElement.getEnclosingElement();

						if (typeElement.getNestingKind() == NestingKind.MEMBER && processHolder.getGeneratedClassHolder(enclosingElement) == null) {
							isElementRemaining = true;
						} else {
							GeneratedClassHolder generatedClassHolder = generatingAnnotationHandler.createGeneratedClassHolder(environment, typeElement);
							processHolder.put(annotatedElement, generatedClassHolder);
							generatingAnnotationHandler.process(annotatedElement, generatedClassHolder);
						}
					}
				} else {
					LOGGER.trace("Skip element {} because it's abstract", annotatedElement);
				}
			}
			/*
			 * We currently do not take into account class annotations from
			 * ancestors. We should careful design the priority rules first.
			 */
		}
		return isElementRemaining;
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy