src/main/java/com/cosium/openapi/annotation_processor/option/IOptions.java
@Value.Immutable
public interface IOptions {

    /**
     * @return The base package that will be used to generate all resources
     */
    String baseGenerationPackage();

    /**
     * @return The specification generator options
     */
    ISpecificationGeneratorOptions specificationGenerator();

    /**
     * @return The code generator options
     */
    ICodeGeneratorOptions codeGenerator();

}
src/main/java/com/cosium/openapi/annotation_processor/code/CodeGeneratorFactory.java
public class CodeGeneratorFactory {

    private final ICodeGeneratorOptions options;
    private final ServiceLoader serviceLoader;

    public CodeGeneratorFactory(ICodeGeneratorOptions options, ServiceLoader serviceLoader) {
        requireNonNull(options);
        requireNonNull(serviceLoader);
        this.options = options;
        this.serviceLoader = serviceLoader;
    }

    public CodeGenerator build(FileManager fileManager) {
        return new DefaultCodeGenerator(
                options,
                serviceLoader,
                fileManager
        );
    }

}
src/main/java/com/cosium/openapi/annotation_processor/loader/ServiceLoader.java
public interface ServiceLoader {

    /**
     * @param serviceType The service type
     * @param <T> The service type
     * @return All the available instances for the service type
     */
    <T> List<T> load(Class<T> serviceType);

}
src/main/java/com/cosium/openapi/annotation_processor/parser/spring/SpringParserFactory.java
public class SpringParserFactory implements PathParserFactory {

    @Override
    public String getSupportedAnnotation() {
        return RequestMapping.class.getCanonicalName();
    }

    @Override
    public PathParser build(Types typeUtils, Elements elementUtils) {
        return new SpringParser(new PropertyUtils(typeUtils, elementUtils), new AnnotationUtils(typeUtils));
    }
}
src/main/java/com/cosium/openapi/annotation_processor/file/FileManagerFactory.java
public class FileManagerFactory {

    private final Filer filer;
    private final String baseGenerationPackage;

    public FileManagerFactory(Filer filer, String baseGenerationPackage) {
        requireNonNull(filer);
        requireNonNull(baseGenerationPackage);
        this.filer = filer;
        this.baseGenerationPackage = baseGenerationPackage;
    }

    public FileManager build(String packageName, Collection<Element> originatingElements) {
        return new DefaultFileManager(baseGenerationPackage + "." + packageName, filer, originatingElements);
    }

}
src/main/java/com/cosium/openapi/annotation_processor/option/OptionsBuilder.java
public class OptionsBuilder {

    private static final String PREFIX = "com.cosium.openapi.";

    private static final String BASE_GENERATION_PACKAGE = PREFIX + "generation_package";

    private static final String SPECIFICATION_GENERATOR_PREFIX = PREFIX + "specification_generator.";

    private static final String SPECIFICATION_GENERATOR_TITLE_OPTION = SPECIFICATION_GENERATOR_PREFIX + "title";
    private static final String SPECIFICATION_GENERATOR_BASE_PATH_OPTION = SPECIFICATION_GENERATOR_PREFIX + "base_path";
    private static final String SPECIFICATION_GENERATOR_PRODUCES_OPTION = SPECIFICATION_GENERATOR_PREFIX + "produces";
    private static final String SPECIFICATION_GENERATOR_CONSUMES_OPTION = SPECIFICATION_GENERATOR_PREFIX + "consumes";

    private static final String CODE_GENERATOR_PREFIX = PREFIX + "code_generator.";

    private static final String CODE_GENERATOR_LANGUAGES_OPTIONS = CODE_GENERATOR_PREFIX + "languages";
    private static final String CODE_GENERATOR_ONE_GENERATION_FOLDER_PER_LANGUAGE_OPTIONS = CODE_GENERATOR_PREFIX + "one_generation_folder_per_language";

    private static final String APPLICATION_JSON = "application/json";

    public Set<String> getSupportedOptions() {
        return new LinkedHashSet<>(Arrays.asList(
                BASE_GENERATION_PACKAGE,
                SPECIFICATION_GENERATOR_TITLE_OPTION,
                SPECIFICATION_GENERATOR_BASE_PATH_OPTION,
                SPECIFICATION_GENERATOR_PRODUCES_OPTION,
                SPECIFICATION_GENERATOR_CONSUMES_OPTION,
                CODE_GENERATOR_LANGUAGES_OPTIONS,
                CODE_GENERATOR_ONE_GENERATION_FOLDER_PER_LANGUAGE_OPTIONS
        ));
    }

    public IOptions build(Map<String, String> options) {
        return Options
                .builder()
                .baseGenerationPackage(options.getOrDefault(BASE_GENERATION_PACKAGE, "com.cosium.openapi.generated"))
                .specificationGenerator(buildSpecificationGenerator(options))
                .codeGenerator(buildCodeGenerator(options))
                .build();
    }

    private ISpecificationGeneratorOptions buildSpecificationGenerator(Map<String, String> options) {
        SpecificationGeneratorOptions.BuildFinal documentatorOptionsBuilder = SpecificationGeneratorOptions
                .builder()
                .title(options.getOrDefault(SPECIFICATION_GENERATOR_TITLE_OPTION, StringUtils.EMPTY))
                .basePath(options.getOrDefault(SPECIFICATION_GENERATOR_BASE_PATH_OPTION, "/"));

        Stream.of(StringUtils
                .split(StringUtils.defaultIfBlank(options.get(SPECIFICATION_GENERATOR_CONSUMES_OPTION), APPLICATION_JSON), ","))
                .forEach(documentatorOptionsBuilder::addConsumes);
        Stream.of(StringUtils
                .split(StringUtils.defaultIfBlank(options.get(SPECIFICATION_GENERATOR_PRODUCES_OPTION), APPLICATION_JSON), ","))
                .forEach(documentatorOptionsBuilder::addProduces);
        return documentatorOptionsBuilder.build();
    }

    private ICodeGeneratorOptions buildCodeGenerator(Map<String, String> options) {
        CodeGeneratorOptions.BuildFinal builder = CodeGeneratorOptions.builder()
                .oneGenerationFolderPerLanguage(Boolean.valueOf(
                        options.getOrDefault(CODE_GENERATOR_ONE_GENERATION_FOLDER_PER_LANGUAGE_OPTIONS, String.valueOf(true))
                ));

        ofNullable(options.get(CODE_GENERATOR_LANGUAGES_OPTIONS))
                .filter(StringUtils::isNotBlank)
                .map(s -> StringUtils.split(s, ","))
                .ifPresent(builder::addLanguages);

        return builder.build();
    }

}
src/main/java/com/cosium/openapi/annotation_processor/file/FileManager.java
public interface FileManager {

    FileObject getResource(CharSequence relativeName) throws NoSuchFileException;

    FileObject getResource(CharSequence pkg, CharSequence relativeName) throws NoSuchFileException;

    FileObject createResource(CharSequence relativeName);

    FileObject createResource(CharSequence pkg, CharSequence relativeName);

}
src/main/java/com/cosium/openapi/annotation_processor/specification/SpecificationGenerator.java
public interface SpecificationGenerator {

    /**
     * Generate specification for given paths
     *
     * @param parsedPaths The parsed paths
     * @param roundDescriptor The descriptor of the current round
     * @return The generated specification
     */
    Swagger generate(List<ParsedPath> parsedPaths, RoundDescriptor roundDescriptor);

}
src/main/java/com/cosium/openapi/annotation_processor/loader/DefaultServiceLoader.java
public class DefaultServiceLoader implements ServiceLoader {

    private final Map<Class<?>, List<?>> servicesByType = Collections.synchronizedMap(new HashMap<>());

    @SuppressWarnings("unchecked")
    @Override
    public <T> List<T> load(Class<T> serviceType) {
        servicesByType.computeIfAbsent(serviceType, this::doLoad);
        return (List<T>) servicesByType.get(serviceType);
    }

    private <T> List<T> doLoad(Class<T> serviceType) {
        try (InputStream inputStream = getClass().getResourceAsStream("/META-INF/services/" + serviceType.getCanonicalName())) {
            return readLines(inputStream)
                    .stream()
                    .map(this::classForName)
                    .map(this::newInstance)
                    .map(serviceType::cast)
                    .collect(Collectors.toList());
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @SuppressWarnings("unchecked")
    private List<String> readLines(InputStream inputStream) throws IOException {
        return IOUtils.readLines(inputStream);
    }

    private Class<?> classForName(String name) {
        try {
            return Class.forName(name);
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        }
    }

    private <T> T newInstance(Class<T> type) {
        try {
            return type.newInstance();
        } catch (InstantiationException | IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }
}
src/main/java/com/cosium/openapi/annotation_processor/parser/PathParser.java
public interface PathParser {

    /**
     * @param annotatedElement The element from which to parse the paths.
     *                         This element is annoted with {@link PathParserFactory#getSupportedAnnotation()}.
     * @return A list of parsed paths
     */
    List<ParsedPath> parse(Element annotatedElement);

}
src/main/java/com/cosium/openapi/annotation_processor/model/ParsedPath.java
public class ParsedPath {

    private final String pathTemplate;
    private final Path path;

    @JsonCreator
    public ParsedPath(@JsonProperty("pathTemplate") String pathTemplate, @JsonProperty("path") Path path) {
        requireNonNull(pathTemplate);
        requireNonNull(path);

        this.pathTemplate = pathTemplate;
        this.path = path;
    }

    public String getPathTemplate() {
        return pathTemplate;
    }

    public Path getPath() {
        return path;
    }

    @Override
    public String toString() {
        return new ToStringBuilder(this)
                .append("pathTemplate", pathTemplate)
                .toString();
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        ParsedPath that = (ParsedPath) o;

        return pathTemplate.equals(that.pathTemplate);
    }

    @Override
    public int hashCode() {
        return pathTemplate.hashCode();
    }
}
src/main/java/com/cosium/openapi/annotation_processor/parser/PathParserFactory.java
public interface PathParserFactory {

    /**
     * @return The supported annotation qualified name
     */
    String getSupportedAnnotation();

    /**
     * @return A new parser
     */
    PathParser build(Types typeUtils, Elements elementUtils);

}
src/main/java/com/cosium/openapi/annotation_processor/code/CodeGenerator.java
public interface CodeGenerator {

    /**
     * Generates code from specification
     * @param swagger The specification to generate code from
     */
    void generate(Swagger swagger);

}
src/main/java/com/cosium/openapi/annotation_processor/specification/SpecificationGeneratorFactory.java
public class SpecificationGeneratorFactory {

    private final AtomicReference<Swagger> runtimeCache = new AtomicReference<>();
    private final ISpecificationGeneratorOptions options;

    public SpecificationGeneratorFactory(ISpecificationGeneratorOptions options) {
        requireNonNull(options);
        this.options = options;
    }


    public SpecificationGenerator build(FileManager fileManager) {
        return new WriterSpecificationGenerator(
                new IncrementalSpecificationGenerator(
                        new BasicSpecificationGenerator(
                                runtimeCache,
                                options
                        ), fileManager
                ), fileManager
        );
    }

}


Question: package com.cosium.openapi.annotation_processor;
import static java.util.Objects.requireNonNull;
import com.cosium.logging.annotation_processor.AbstractLoggingProcessor;
import com.cosium.openapi.annotation_processor.code.CodeGenerator;
import com.cosium.openapi.annotation_processor.code.CodeGeneratorFactory;
import com.cosium.openapi.annotation_processor.file.FileManager;
import com.cosium.openapi.annotation_processor.file.FileManagerFactory;
import com.cosium.openapi.annotation_processor.loader.DefaultServiceLoader;
import com.cosium.openapi.annotation_processor.loader.ServiceLoader;
import com.cosium.openapi.annotation_processor.model.ParsedPath;
import com.cosium.openapi.annotation_processor.option.IOptions;
import com.cosium.openapi.annotation_processor.option.OptionsBuilder;
import com.cosium.openapi.annotation_processor.parser.PathParser;
import com.cosium.openapi.annotation_processor.parser.PathParserFactory;
import com.cosium.openapi.annotation_processor.parser.spring.SpringParserFactory;
import com.cosium.openapi.annotation_processor.specification.SpecificationGenerator;
import com.cosium.openapi.annotation_processor.specification.SpecificationGeneratorFactory;
import com.google.auto.service.AutoService;
import io.swagger.models.Swagger;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
import javax.annotation.processing.Messager;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.Processor;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
import javax.tools.Diagnostic;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;



/**
 * Created on 12/07/17.
 *
 * @author Reda.Housni-Alaoui
 */
@AutoService(Processor.class)
public class OpenAPIProcessor extends AbstractLoggingProcessor {

    private static final Logger LOG = LoggerFactory.getLogger(OpenAPIProcessor.class);

    private final AtomicInteger roundNumber = new AtomicInteger();
    private final List<PathParserFactory> parserFactories = new ArrayList<>();
    private final OptionsBuilder optionsBuilder = new OptionsBuilder();
    private final ServiceLoader serviceLoader = new DefaultServiceLoader();

    private Types typeUtils;
    private Elements elementUtils;
    private Messager messager;

    private List<ParserHolder> pathParsers;
    private FileManagerFactory fileManagerFactory;
    private SpecificationGeneratorFactory specificationGeneratorFactory;
    private CodeGeneratorFactory codeGeneratorFactory;

    public OpenAPIProcessor() {
        parserFactories.add(new SpringParserFactory());
    }

    @Override
    public Set<String> getSupportedAnnotationTypes() {
        return parserFactories
                .stream()
                .map(PathParserFactory::getSupportedAnnotation)
                .collect(Collectors.toSet());
    }

    @Override
    public Set<String> getSupportedOptions() {
        return optionsBuilder.getSupportedOptions();
    }

    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
        super.init(processingEnv);
        this.typeUtils = processingEnv.getTypeUtils();
        this.elementUtils = processingEnv.getElementUtils();
        this.messager = processingEnv.getMessager();

        this.pathParsers = parserFactories
                .stream()
                .map(ParserHolder::new)
                .collect(Collectors.toList());
        IOptions options = optionsBuilder.build(processingEnv.getOptions());
        this.fileManagerFactory = new FileManagerFactory(processingEnv.getFiler(), options.baseGenerationPackage());
        this.specificationGeneratorFactory = new SpecificationGeneratorFactory(options.specificationGenerator());
        this.codeGeneratorFactory = new CodeGeneratorFactory(options.codeGenerator(), serviceLoader);
    }

    @Override
    protected boolean doProcess(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        boolean lastRound = roundEnv.processingOver();
        if (lastRound) {
            LOG.debug("Processing last round");
        } else {
            LOG.debug("Processing");
        }
        AtomicReference<Element> currentAnnotatedElement = new AtomicReference<>();
        try {
            doProcess(roundEnv, currentAnnotatedElement, new RoundDescriptor(roundNumber.incrementAndGet(), lastRound));
        } catch (Throwable e) {
            LOG.error(e.getMessage(), e);
            messager.printMessage(Diagnostic.Kind.ERROR, e.getMessage(), currentAnnotatedElement.get());
            if(!(e instanceof Exception)){
                throw e;
            }
        }
        return true;
    }

    private void doProcess(RoundEnvironment roundEnv, AtomicReference<Element> currentAnnotatedElement, RoundDescriptor roundDescriptor) {
        Collection<Element> originatingElements = new HashSet<>();
        List<ParsedPath> parsedPaths = pathParsers
                .stream()
                .flatMap(parserHolder -> {
                    TypeElement annotation = elementUtils.getTypeElement(parserHolder.getSupportedAnnotation());
Answer:                     PathParser pathParser = parserHolder.getPathParser();