All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.helidon.codegen.CodegenFiler Maven / Gradle / Ivy

/*
 * Copyright (c) 2023, 2024 Oracle and/or its affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package io.helidon.codegen;

import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Collectors;

import io.helidon.codegen.classmodel.ClassModel;
import io.helidon.common.types.TypeName;

/**
 * An abstraction for writing out source files and resource files.
 * Always attempts to create a new file and replace its content (as it is impossible to update files in annotation processing).
 */
public interface CodegenFiler {
    /**
     * Write a source file from its {@link io.helidon.codegen.classmodel.ClassModel}.
     *
     * @param classModel          class model to write out
     * @param originatingElements elements that caused this type to be generated
     *                            (you can use {@link io.helidon.common.types.TypeInfo#originatingElement()} for example
     * @return written path, we expect to always run on local file system
     */
    Path writeSourceFile(ClassModel classModel, Object... originatingElements);

    /**
     * Write a resource file.
     *
     * @param resource bytes of the resource file
     * @param location location to write to in the classes output directory
     * @param originatingElements elements that caused this file to be generated
     * @return written path, we expect to always run on local file system
     */
    Path writeResource(byte[] resource, String location, Object... originatingElements);

    /**
     * A text resource that can be updated in the output.
     * Note that the resource can only be written once per processing round.
     *
     * @param location            location to read/write to in the classes output directory
     * @param originatingElements elements that caused this file to be generated
     * @return the resource that can be used to update the file
     */
    default FilerTextResource textResource(String location, Object... originatingElements) {
        throw new UnsupportedOperationException("Method textResource not implemented yet on " + getClass().getName());
    }

    /**
     * A text resource that can be updated in the output.
     * Note that the resource can only be written once per processing round.
     *
     * @param location            location to read/write to in the classes output directory
     * @param originatingElements elements that caused this file to be generated
     * @return the resource that can be used to update the file
     */
    default FilerResource resource(String location, Object... originatingElements) {
        throw new UnsupportedOperationException("Method resource not implemented yet on " + getClass().getName());
    }

    /**
     * Write a {@code META-INF/services} file for a specific provider interface and implementation(s).
     *
     * @param generator type of the generator (to mention in the generated code)
     * @param providerInterface type of the provider interface (and also name of the file to be generated)
     * @param providers list of provider implementations to add to the file
     * @param originatingElements elements that caused this type to be generated
     */
    default void services(TypeName generator,
                          TypeName providerInterface,
                          List providers,
                          Object... originatingElements) {
        Objects.requireNonNull(generator);
        Objects.requireNonNull(providerInterface);
        Objects.requireNonNull(providers);

        if (providers.isEmpty()) {
            // do not write services file if there is no provider added
            return;
        }

        String location = "META-INF/services/" + providerInterface.fqName();

        var resource = textResource(location, originatingElements);

        List lines = new ArrayList<>(resource.lines());
        Set existingServices = lines.stream()
                .map(String::trim)
                .filter(Predicate.not(it -> it.startsWith("#")))
                .map(TypeName::create)
                .collect(Collectors.toSet());

        if (lines.isEmpty()) {
            // @Generated
            lines.add("# " + GeneratedAnnotationHandler.create(generator,
                                                               providers.getFirst(),
                                                               TypeName.create(
                                                                       "MetaInfServicesModuleComponent"),
                                                               "1",
                                                               ""));
        }

        for (TypeName provider : providers) {
            if (existingServices.add(provider)) {
                // only add the provider if it does not yet exist
                lines.add(provider.fqName());
            }
        }

        resource.lines(lines);
        resource.write();
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy