001/*
002 * Copyright (C) 2022 - 2023, the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *    http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package io.github.ascopes.jct.filemanagers.config;
017
018import io.github.ascopes.jct.compilers.JctCompiler;
019import io.github.ascopes.jct.filemanagers.AnnotationProcessorDiscovery;
020import io.github.ascopes.jct.filemanagers.JctFileManager;
021import java.util.Map;
022import javax.tools.StandardLocation;
023import org.apiguardian.api.API;
024import org.apiguardian.api.API.Status;
025import org.slf4j.Logger;
026import org.slf4j.LoggerFactory;
027
028/**
029 * Configurer for a file manager that makes annotation processors in the classpath accessible to the
030 * annotation processor path.
031 *
032 * <p>If annotation processor discovery is disabled for dependencies, this will be skipped.
033 *
034 * @author Ashley Scopes
035 * @since 0.0.1
036 */
037@API(since = "0.0.1", status = Status.STABLE)
038public final class JctFileManagerAnnotationProcessorClassPathConfigurer implements
039    JctFileManagerConfigurer {
040
041  private static final Logger LOGGER = LoggerFactory
042      .getLogger(JctFileManagerAnnotationProcessorClassPathConfigurer.class);
043
044  private static final Map<StandardLocation, StandardLocation> INHERITED_AP_PATHS = Map.of(
045      // https://stackoverflow.com/q/53084037
046      // Seems that javac will always use the classpath to implement this behaviour, and never
047      // the module path. Let's keep this simple and mimic this behaviour. If someone complains
048      // about it being problematic in the future, then I am open to change how this works to
049      // keep it sensible.
050      // (from -> to)
051      StandardLocation.CLASS_PATH, StandardLocation.ANNOTATION_PROCESSOR_PATH
052  );
053
054  private final JctCompiler<?, ?> compiler;
055
056  /**
057   * Initialise the configurer with the desired compiler.
058   *
059   * @param compiler the compiler to wrap.
060   */
061  public JctFileManagerAnnotationProcessorClassPathConfigurer(JctCompiler<?, ?> compiler) {
062    this.compiler = compiler;
063  }
064
065  @Override
066  public JctFileManager configure(JctFileManager fileManager) {
067    LOGGER.debug("Configuring annotation processor discovery mechanism");
068
069    switch (compiler.getAnnotationProcessorDiscovery()) {
070      case ENABLED:
071        LOGGER.trace("Annotation processor discovery is enabled, ensuring empty location exists");
072
073        INHERITED_AP_PATHS.values().forEach(fileManager::createEmptyLocation);
074
075        return fileManager;
076
077      case INCLUDE_DEPENDENCIES:
078        LOGGER.trace("Annotation processor discovery is enabled, copying classpath dependencies "
079            + "into the annotation processor path");
080
081        INHERITED_AP_PATHS.forEach(fileManager::copyContainers);
082        INHERITED_AP_PATHS.values().forEach(fileManager::createEmptyLocation);
083
084        return fileManager;
085
086      default:
087        throw new IllegalStateException("Cannot configure annotation processor discovery");
088    }
089  }
090
091  @Override
092  public boolean isEnabled() {
093    return compiler.getAnnotationProcessorDiscovery() != AnnotationProcessorDiscovery.DISABLED;
094  }
095}