001/* 002 * Copyright (C) 2022 Ashley Scopes 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.filemanagers.JctFileManager; 019import java.util.ArrayDeque; 020import java.util.Deque; 021import java.util.List; 022import org.slf4j.Logger; 023import org.slf4j.LoggerFactory; 024 025/** 026 * A collection representing a chain of configurers to apply to a file manager. 027 * 028 * <p>This is used to provide a set of ordered configuration operations to perform on newly 029 * created file managers to prepare them for consumption in test cases. Common operations 030 * may include: 031 * 032 * <ul> 033 * <li>Automatically configuring the annotation processor paths;</li> 034 * <li>Creating empty locations to prevent compilers raising exceptions;</li> 035 * <li>Installing logging interceptors.</li> 036 * </ul> 037 * 038 * <p>When configuring a file manager with this chain, each configurer is invoked in the 039 * provided order. Configurers themselves must return a file manager as a result of the 040 * configure operation. This operation may return the input file manager if the operation 041 * mutated the input, or it may return a different file manager instance. In the latter 042 * case, subsequent configuration operations internally will use the new file manager. 043 * The last returned file manager in the chain will be used as the final returned result. 044 * This enables configurers to wrap file managers in proxies or delegating implementations 045 * to intercept or override existing behaviours. 046 * 047 * <p>This class is not thread-safe. 048 * 049 * @author Ashley Scopes 050 * @since 0.0.1 051 */ 052public final class JctFileManagerConfigurerChain { 053 054 private static final Logger log = LoggerFactory.getLogger(JctFileManagerConfigurerChain.class); 055 056 private final Deque<JctFileManagerConfigurer> configurers; 057 058 /** 059 * Initialise this chain. 060 */ 061 public JctFileManagerConfigurerChain() { 062 configurers = new ArrayDeque<>(); 063 } 064 065 /** 066 * Add a configurer to the start of the chain. 067 * 068 * @param configurer the configurer to add. 069 * @return this chain for further calls. 070 */ 071 public JctFileManagerConfigurerChain addFirst(JctFileManagerConfigurer configurer) { 072 configurers.addFirst(configurer); 073 return this; 074 } 075 076 /** 077 * Add a configurer to the end of the chain. 078 * 079 * @param configurer the configurer to add. 080 * @return this chain for further calls. 081 */ 082 public JctFileManagerConfigurerChain addLast(JctFileManagerConfigurer configurer) { 083 configurers.addLast(configurer); 084 return this; 085 } 086 087 /** 088 * Get an immutable copy of the list of configurers. 089 * 090 * @return the list of configurers. 091 */ 092 public List<JctFileManagerConfigurer> list() { 093 return List.copyOf(configurers); 094 } 095 096 /** 097 * Apply each configurer to the given file manager in order. 098 * 099 * @param fileManager the file manager to configure. 100 * @return the configured file manager to use. This may or may not be the same object as the input 101 * parameter, depending on how the configurers manipulate the input object. 102 */ 103 public JctFileManager configure(JctFileManager fileManager) { 104 for (var configurer : configurers) { 105 if (configurer.isEnabled()) { 106 log.debug("Applying {} to file manager {}", configurer, fileManager); 107 // Configurers can totally replace the existing file manager 108 // if they choose. 109 fileManager = configurer.configure(fileManager); 110 } else { 111 log.trace("Skipping {}", configurer); 112 } 113 } 114 return fileManager; 115 } 116}