/*
* Copyright 2016 ArcBees Inc.
*
* 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.gwtplatform.mvp.processors.entrypoint;
import java.lang.annotation.Annotation;
import java.util.Collection;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.SimpleAnnotationValueVisitor7;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableList;
import com.gwtplatform.mvp.client.DefaultBootstrapper;
import com.gwtplatform.mvp.client.annotations.UseBootstrapper;
import com.gwtplatform.mvp.client.annotations.UsePreBootstrapper;
import com.gwtplatform.processors.tools.domain.HasImports;
import com.gwtplatform.processors.tools.domain.HasType;
import com.gwtplatform.processors.tools.domain.Type;
import com.gwtplatform.processors.tools.exceptions.UnableToProcessException;
import com.gwtplatform.processors.tools.logger.Logger;
import static com.google.auto.common.AnnotationMirrors.getAnnotationValue;
import static com.google.auto.common.MoreElements.getAnnotationMirror;
import static com.google.auto.common.MoreTypes.asDeclared;
import static com.google.auto.common.MoreTypes.asTypeElement;
import static com.google.common.base.Optional.fromNullable;
import static com.google.common.base.Predicates.or;
import static com.gwtplatform.processors.tools.utils.ElementPredicates.IS_ACCESSIBLE_CLASS;
import static com.gwtplatform.processors.tools.utils.ElementPredicates.IS_DEFAULT_CONSTRUCTOR;
import static com.gwtplatform.processors.tools.utils.ElementPredicates.IS_INJECTABLE_CONSTRUCTOR;
import static com.gwtplatform.processors.tools.utils.ElementPredicates.hasMatchingConstructorOrNone;
public class EntryPoint implements HasType, HasImports {
private static final Type DEFAULT_BOOTSTRAPPER_TYPE = new Type(DefaultBootstrapper.class);
private static final Type TYPE = new Type("com.gwtplatform.mvp.client", "GeneratedMvpEntryPoint");
private final Logger logger;
private final TypeElement element;
private Type bootstrapper;
private Optional<Type> preBootstrapper;
public EntryPoint(
Logger logger,
TypeElement element) {
this.logger = logger;
this.element = element;
}
@Override
public Type getType() {
return TYPE;
}
public Type getBootstrapper() {
if (bootstrapper == null) {
bootstrapper = extractBootstrapperType();
}
return bootstrapper;
}
private Type extractBootstrapperType() {
DeclaredType bootstrapperMirror = getTypeMirrorFromAnnotation(UseBootstrapper.class);
if (bootstrapperMirror != null) {
validateBootstrapper(bootstrapperMirror);
return new Type(bootstrapperMirror);
}
return DEFAULT_BOOTSTRAPPER_TYPE;
}
private void validateBootstrapper(DeclaredType bootstrapper) {
TypeElement typeElement = asTypeElement(bootstrapper);
if (!IS_ACCESSIBLE_CLASS.apply(typeElement)) {
logger.error().context(typeElement)
.log("Declared bootstrapper must be a public, non-abstract class.");
throw new UnableToProcessException();
}
if (!hasMatchingConstructorOrNone(or(IS_DEFAULT_CONSTRUCTOR, IS_INJECTABLE_CONSTRUCTOR)).apply(typeElement)) {
logger.error().context(typeElement)
.log("Declared bootstrapper must have a public, no-arg, or @Inject constructor.");
throw new UnableToProcessException();
}
}
public Type getPreBootstrapper() {
if (preBootstrapper == null) {
preBootstrapper = fromNullable(extractPreBootstrapperType());
}
return preBootstrapper.orNull();
}
private Type extractPreBootstrapperType() {
DeclaredType preBootstrapperMirror = getTypeMirrorFromAnnotation(UsePreBootstrapper.class);
if (preBootstrapperMirror != null) {
validatePreBootstrapper(preBootstrapperMirror);
return new Type(preBootstrapperMirror);
}
return null;
}
private void validatePreBootstrapper(DeclaredType preBootstrapper) {
TypeElement typeElement = asTypeElement(preBootstrapper);
if (!IS_ACCESSIBLE_CLASS.apply(typeElement)) {
logger.error().context(typeElement)
.log("Declared pre-bootstrapper must be a public, non-abstract class.");
throw new UnableToProcessException();
}
if (!hasMatchingConstructorOrNone(IS_DEFAULT_CONSTRUCTOR).apply(typeElement)) {
logger.error().context(typeElement)
.log("Declared pre-bootstrapper must have a public, no-arg constructor.");
throw new UnableToProcessException();
}
}
private DeclaredType getTypeMirrorFromAnnotation(Class<? extends Annotation> annotationClass) {
Optional<AnnotationMirror> annotation = getAnnotationMirror(element, annotationClass);
if (annotation.isPresent()) {
return getAnnotationValue(annotation.get(), "value")
.accept(new SimpleAnnotationValueVisitor7<DeclaredType, Void>(null) {
@Override
public DeclaredType visitType(TypeMirror typeMirror, Void nothing) {
return asDeclared(typeMirror);
}
}, null);
}
return null;
}
@Override
public Collection<String> getImports() {
ImmutableList.Builder<String> imports = ImmutableList.<String>builder()
.add(getBootstrapper().getQualifiedName());
if (getPreBootstrapper() != null) {
imports.add(getPreBootstrapper().getQualifiedName());
}
return imports.build();
}
}