001/*
002 * Copyright (C) 2022 - 2024, 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.ex.JctIllegalInputException;
020import io.github.ascopes.jct.filemanagers.AnnotationProcessorDiscovery;
021import io.github.ascopes.jct.filemanagers.JctFileManager;
022import java.util.Map;
023import javax.tools.StandardLocation;
024import org.slf4j.Logger;
025import org.slf4j.LoggerFactory;
026
027/**
028 * Configurer for a file manager that makes annotation processors in the classpath accessible to the
029 * annotation processor path.
030 *
031 * <p>If annotation processor discovery is disabled for dependencies, this will be skipped.
032 *
033 * @author Ashley Scopes
034 * @since 0.0.1
035 */
036public final class JctFileManagerAnnotationProcessorClassPathConfigurer
037    implements JctFileManagerConfigurer {
038
039  private static final Logger log = LoggerFactory
040      .getLogger(JctFileManagerAnnotationProcessorClassPathConfigurer.class);
041
042  private static final Map<StandardLocation, StandardLocation> INHERITED_AP_PATHS = Map.of(
043      // https://stackoverflow.com/q/53084037
044      // Seems that javac will always use the classpath to implement this behaviour, and never
045      // the module path. Let's keep this simple and mimic this behaviour. If someone complains
046      // about it being problematic in the future, then I am open to change how this works to
047      // keep it sensible.
048      // (from -> to)
049      StandardLocation.CLASS_PATH, StandardLocation.ANNOTATION_PROCESSOR_PATH
050  );
051
052  private final JctCompiler compiler;
053
054  /**
055   * Initialise the configurer with the desired compiler.
056   *
057   * @param compiler the compiler to wrap.
058   */
059  public JctFileManagerAnnotationProcessorClassPathConfigurer(JctCompiler compiler) {
060    this.compiler = compiler;
061  }
062
063  @Override
064  public JctFileManager configure(JctFileManager fileManager) {
065    log.debug("Configuring annotation processor discovery mechanism");
066
067    switch (compiler.getAnnotationProcessorDiscovery()) {
068      case ENABLED:
069        log.trace("Annotation processor discovery is enabled, ensuring empty location exists");
070
071        INHERITED_AP_PATHS.values().forEach(fileManager::createEmptyLocation);
072
073        return fileManager;
074
075      case INCLUDE_DEPENDENCIES:
076        log.trace("Annotation processor discovery is enabled, copying classpath dependencies "
077            + "into the annotation processor path");
078
079        INHERITED_AP_PATHS.forEach(fileManager::copyContainers);
080        INHERITED_AP_PATHS.values().forEach(fileManager::createEmptyLocation);
081
082        return fileManager;
083
084      default:
085        throw new JctIllegalInputException("Cannot configure annotation processor discovery");
086    }
087  }
088
089  @Override
090  public boolean isEnabled() {
091    return compiler.getAnnotationProcessorDiscovery() != AnnotationProcessorDiscovery.DISABLED;
092  }
093}