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.JctFileManager;
020import io.github.ascopes.jct.utils.SpecialLocationUtils;
021import io.github.ascopes.jct.utils.StringUtils;
022import io.github.ascopes.jct.workspaces.impl.WrappingDirectoryImpl;
023import javax.tools.StandardLocation;
024import org.apiguardian.api.API;
025import org.apiguardian.api.API.Status;
026import org.slf4j.Logger;
027import org.slf4j.LoggerFactory;
028
029/**
030 * Configurer for a file manager that detects and applies classpath paths that contain JPMS modules
031 * to the module path.
032 *
033 * <p>If classpath inheritance or module fixing is disabled in the compiler,
034 * this will not run.
035 *
036 * <p>This fixes some common configuration issues when IDEs invoke JUnit.
037 *
038 * @author Ashley Scopes
039 * @since 0.0.1
040 */
041@API(since = "0.0.1", status = Status.STABLE)
042public final class JctFileManagerJvmClassPathModuleConfigurer implements JctFileManagerConfigurer {
043
044  private static final Logger LOGGER = LoggerFactory
045      .getLogger(JctFileManagerJvmClassPathModuleConfigurer.class);
046
047  private final JctCompiler compiler;
048
049  /**
050   * Initialise the configurer with the desired compiler.
051   *
052   * @param compiler the compiler to wrap.
053   */
054  public JctFileManagerJvmClassPathModuleConfigurer(JctCompiler compiler) {
055    this.compiler = compiler;
056  }
057
058  @Override
059  public JctFileManager configure(JctFileManager fileManager) {
060    LOGGER.debug(
061        "Copying any misplaced modules that exist within the class path onto the module path"
062    );
063
064    SpecialLocationUtils
065        .currentClassPathLocations()
066        .stream()
067        .peek(loc -> LOGGER
068            .atTrace()
069            .setMessage("Adding {} ({}) to file manager module path (inherited from JVM))")
070            .addArgument(() -> StringUtils.quoted(loc.toAbsolutePath()))
071            .addArgument(() -> StringUtils.quoted(loc.toUri()))
072            .log())
073        .map(WrappingDirectoryImpl::new)
074        // File manager will pull out the actual modules automatically.
075        .forEach(dir -> fileManager.addPath(StandardLocation.MODULE_PATH, dir));
076
077    return fileManager;
078  }
079
080  @Override
081  public boolean isEnabled() {
082    return compiler.isInheritClassPath() && compiler.isFixJvmModulePathMismatch();
083  }
084}