/**
* 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<AnnotatedAndRootElements> 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<? extends Element> 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 <T extends GeneratedClassHolder> void processThrowing(AnnotationHandler<T> 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<? extends Element> 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;
}
}