/* * 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; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; import java.util.stream.Collectors; import javax.annotation.processing.Processor; import javax.annotation.processing.RoundEnvironment; import javax.inject.Singleton; import javax.lang.model.element.Element; import javax.lang.model.element.ElementKind; import javax.lang.model.element.Modifier; import javax.lang.model.element.TypeElement; import javax.lang.model.type.TypeMirror; import com.google.auto.common.MoreElements; import com.google.auto.service.AutoService; import com.gwtplatform.common.processors.AbstractGwtpAppProcessor; import com.gwtplatform.mvp.client.annotations.DefaultGatekeeper; import com.gwtplatform.mvp.client.proxy.AlwaysTrueGatekeeper; import com.gwtplatform.mvp.client.proxy.Gatekeeper; import com.gwtplatform.processors.tools.SupportedAnnotationClasses; import com.gwtplatform.processors.tools.bindings.BindingsProcessors; import com.gwtplatform.processors.tools.domain.Type; import com.gwtplatform.processors.tools.exceptions.UnableToProcessException; import com.gwtplatform.processors.tools.utils.MetaInfResource; import static com.google.auto.common.MoreElements.hasModifiers; import static com.gwtplatform.common.processors.module.GwtpAppModuleProcessor.MAIN_MODULE_TYPE; import static com.gwtplatform.processors.tools.bindings.BindingContext.newAnnotatedBinding; @AutoService(Processor.class) @SupportedAnnotationClasses(DefaultGatekeeper.class) public class DefaultGatekeeperProcessor extends AbstractGwtpAppProcessor { private static final String META_INF_FILE_NAME = "gwtp/defaultGatekeeper"; private static final Type DEFAULT_GATEKEEPER = new Type(AlwaysTrueGatekeeper.class); private BindingsProcessors bindingsProcessors; private MetaInfResource metaInfResource; private boolean generated; private boolean metaDataProcessed; @Override protected void initSafe() { bindingsProcessors = new BindingsProcessors(logger, utils, outputter); metaInfResource = new MetaInfResource(logger, outputter, META_INF_FILE_NAME); } @Override protected void processAsApp(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { Set<TypeElement> gatekeepers = extractGatekeepers(roundEnv); if (!gatekeepers.isEmpty()) { Type gatekeeper = asGatekeeper(gatekeepers.iterator().next()); createBinding(gatekeeper); generated = true; } else if (!generated) { if (!metaDataProcessed) { maybeGenerateFromMetaData(); metaDataProcessed = true; } if (!generated && roundEnv.processingOver()) { createBinding(DEFAULT_GATEKEEPER); } } } @Override protected void processAsModule(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { Set<TypeElement> gatekeepers = extractGatekeepers(roundEnv); if (!gatekeepers.isEmpty()) { Type gatekeeper = asGatekeeper(gatekeepers.iterator().next()); createMetadataFile(gatekeeper); generated = true; } } private Set<TypeElement> extractGatekeepers(RoundEnvironment roundEnv) { Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(DefaultGatekeeper.class); elements = utils.getSourceFilter().filterElements(elements); Set<TypeElement> typeElements = elements.stream() .filter(gatekeeper -> gatekeeper.getKind() == ElementKind.CLASS) .map(MoreElements::asType) .collect(Collectors.toCollection(LinkedHashSet::new)); validateGatekeepers(typeElements); return typeElements; } private void validateGatekeepers(Set<TypeElement> gatekeepers) { if ((!generated && gatekeepers.size() > 1) || (generated && !gatekeepers.isEmpty())) { logger.error() .context(gatekeepers.iterator().next()) .log("Multiple classes annotated with @DefaultGatekeeper. You may only have one or none."); throw new UnableToProcessException(); } } private Type asGatekeeper(TypeElement element) { TypeMirror type = element.asType(); TypeMirror gatekeeperType = utils.createWithWildcard(Gatekeeper.class); if (!hasModifiers(Modifier.ABSTRACT).apply(element) && hasModifiers(Modifier.PUBLIC).apply(element) && utils.getTypes().isSubtype(type, gatekeeperType)) { return new Type(type); } logger.error().context(element) .log("Element annotated with @DefaultGatekeeper is invalid. It must be a public, non-abstract class " + "and implement Gatekeeper."); throw new UnableToProcessException(); } private void createMetadataFile(Type gatekeeper) { metaInfResource.writeLine(gatekeeper.getQualifiedName()); metaInfResource.closeWriter(); logger.debug("Default gatekeeper `%s` written to meta data.", gatekeeper); } private void maybeGenerateFromMetaData() { List<String> defaultGatekeepers = metaInfResource.readAll(); if (defaultGatekeepers.size() > 1) { logger.error().log("Multiple classes annotated with @DefaultGatekeeper found in classpath. " + "To resolve the issue, create a new default Gatekeeper in the current source path."); } else if (!defaultGatekeepers.isEmpty()) { createBinding(new Type(defaultGatekeepers.get(0))); generated = true; } } private void createBinding(Type gatekeeper) { logger.debug("Default gatekeeper bound to `%s`.", gatekeeper); bindingsProcessors.process(newAnnotatedBinding(MAIN_MODULE_TYPE, new Type(Gatekeeper.class), gatekeeper, DefaultGatekeeper.class, Singleton.class)); } }