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

io.helidon.codegen.classmodel.Method Maven / Gradle / Ivy

Go to download

Class model generator designed to be used by code generating components (annotation processors, maven plugins)

There is a newer version: 4.1.4
Show newest version
/*
 * 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.classmodel;

import java.io.IOException;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Supplier;

import io.helidon.common.types.AccessModifier;
import io.helidon.common.types.TypeName;

/**
 * Model of the method which should be created in the specific type.
 */
public final class Method extends Executable {

    private final Map declaredTokens;
    private final boolean isDefault;
    private final boolean isFinal;
    private final boolean isStatic;
    private final boolean isAbstract;

    private Method(Builder builder) {
        super(builder);
        this.isDefault = builder.isDefault;
        this.isFinal = builder.isFinal;
        this.isStatic = builder.isStatic;
        this.isAbstract = builder.isAbstract;
        this.declaredTokens = Collections.unmodifiableMap(new LinkedHashMap<>(builder.declaredTokens));
    }

    /**
     * Create new {@link Builder} instance.
     *
     * @return new builder instance
     */
    public static Builder builder() {
        return new Builder()
                .returnType(builder -> builder.type(void.class));
    }

    @Override
    void writeComponent(ModelWriter writer, Set declaredTokens, ImportOrganizer imports, ClassType classType)
            throws IOException {
        if (javadoc().generate()) {
            javadoc().writeComponent(writer, declaredTokens, imports, classType);
            writer.write("\n");
        }
        for (Annotation annotation : annotations()) {
            annotation.writeComponent(writer, declaredTokens, imports, classType);
            writer.write("\n");
        }
        if (classType == ClassType.INTERFACE) {
            if (isDefault) {
                writer.write("default ");
            } else if (isStatic) {
                writer.write("static ");
            }
        } else {
            if (AccessModifier.PACKAGE_PRIVATE != accessModifier()) {
                writer.write(accessModifier().modifierName() + " ");
            }
            if (isStatic) {
                writer.write("static ");
            }
            if (isFinal) {
                writer.write("final ");
            }
            if (isAbstract) {
                writer.write("abstract ");
            }
        }
        appendTokenDeclaration(writer, declaredTokens, imports, classType);
        type().writeComponent(writer, declaredTokens, imports, classType); //write return type
        writer.write(" " + name() + "(");
        boolean first = true;
        for (Parameter parameter : parameters()) {
            if (first) {
                first = false;
            } else {
                writer.write(", ");
            }
            parameter.writeComponent(writer, declaredTokens, imports, classType);
        }
        writer.write(")");
        writeThrows(writer, declaredTokens, imports, classType);
        if (classType == ClassType.INTERFACE) {
            if (!isDefault && !isStatic) {
                writer.write(";");
                return;
            }
        } else {
            if (isAbstract) {
                writer.write(";");
                return;
            }
        }
        writer.write(" {");
        if (hasBody()) {
            writeBody(writer, imports);
        } else {
            writer.write("\n");
        }
        writer.write("}");
    }

    private void appendTokenDeclaration(ModelWriter writer,
                                        Set declaredTokens,
                                        ImportOrganizer imports,
                                        ClassType classType)
            throws IOException {
        Set tokensToDeclare = new LinkedHashSet<>();
        if (isStatic) {
            for (Parameter parameter : parameters()) {
                if (parameter.type() instanceof TypeArgument typeArgument) {
                    String tokenName = typeArgument.token();
                    if (!tokenName.equals("?")) {
                        tokensToDeclare.add(tokenName);
                    }
                }
            }
        } else {
            for (Parameter parameter : parameters()) {
                if (parameter.type() instanceof TypeArgument typeArgument) {
                    String tokenName = typeArgument.token();
                    if (!declaredTokens.contains(tokenName) && !tokenName.equals("?")) {
                        tokensToDeclare.add(tokenName);
                    }
                }
            }
        }
        if (!tokensToDeclare.isEmpty()) {
            writer.write("<");
            boolean first = true;
            for (String token : tokensToDeclare) {
                if (first) {
                    first = false;
                } else {
                    writer.write(", ");
                }
                if (this.declaredTokens.containsKey(token)) {
                    this.declaredTokens.get(token).writeComponent(writer, declaredTokens, imports, classType);
                } else {
                    writer.write(token);
                }
            }
            for (Map.Entry entry : this.declaredTokens.entrySet()) {
                if (!tokensToDeclare.contains(entry.getKey())) {
                    entry.getValue().writeComponent(writer, declaredTokens, imports, classType);
                }
            }
            writer.write("> ");
        } else if (!this.declaredTokens.isEmpty()) {
            writer.write("<");
            boolean first = true;
            for (Map.Entry entry : this.declaredTokens.entrySet()) {
                if (first) {
                    first = false;
                } else {
                    writer.write(", ");
                }
                entry.getValue().writeComponent(writer, declaredTokens, imports, classType);
            }
            writer.write("> ");
        }
    }

    @Override
    void addImports(ImportOrganizer.Builder imports) {
        super.addImports(imports);
        type().addImports(imports);
    }

    boolean isStatic() {
        return isStatic;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }
        Method method = (Method) o;
        return Objects.equals(type(), method.type())
                && Objects.equals(name(), method.name())
                && parameters().size() == method.parameters().size()
                && parameters().equals(method.parameters());
    }

    @Override
    public int hashCode() {
        return Objects.hash(type(), name(), parameters());
    }

    @Override
    public String toString() {
        return "Method{"
                + "name=" + name()
                + ", isFinal=" + isFinal
                + ", isStatic=" + isStatic
                + ", isAbstract=" + isAbstract
                + ", returnType=" + type().fqTypeName()
                + '}';
    }

    /**
     * Fluent API builder for {@link Method}.
     */
    public static final class Builder extends Executable.Builder {

        private final Map declaredTokens = new LinkedHashMap<>();
        private boolean isDefault = false;
        private boolean isFinal = false;
        private boolean isStatic = false;
        private boolean isAbstract = false;

        Builder() {
        }

        @Override
        public Method build() {
            if (name() == null) {
                throw new ClassModelException("Method needs to have name specified");
            }
            if (isStatic && isAbstract) {
                throw new IllegalStateException("Method cannot be static and abstract at the same time");
            }
            if (isFinal && isAbstract) {
                throw new IllegalStateException("Method cannot be final and abstract at the same time");
            }
            return new Method(this);
        }

        @Override
        public Builder content(List content) {
            declaredTokens.clear();
            return super.content(content);
        }

        /**
         * Whether this method is final.
         *
         * @param isFinal method is final
         * @return updated builder instance
         */
        public Builder isFinal(boolean isFinal) {
            this.isFinal = isFinal;
            return this;
        }

        /**
         * Whether this method is static.
         *
         * @param isStatic method is static
         * @return updated builder instance
         */
        public Builder isStatic(boolean isStatic) {
            this.isStatic = isStatic;
            return this;
        }

        /**
         * Whether this method is abstract.
         *
         * @param isAbstract method is abstract
         * @return updated builder instance
         */
        public Builder isAbstract(boolean isAbstract) {
            this.isAbstract = isAbstract;
            return this;
        }

        /**
         * Whether this method is default.
         *
         * @param isDefault method is default
         * @return updated builder instance
         */
        public Builder isDefault(boolean isDefault) {
            this.isDefault = isDefault;
            return this;
        }

        /**
         * Set return type of the method.
         * Default is {@code void}.
         *
         * @param type return type
         * @return updated builder instance
         */
        public Builder returnType(TypeName type) {
            return type(type);
        }

        /**
         * Set return type of the method.
         * Default is {@code void}.
         *
         * @param type return type
         * @param description return type description
         * @return updated builder instance
         */
        public Builder returnType(TypeName type, String description) {
            return type(type).returnJavadoc(description);
        }

        /**
         * Set return type of the method.
         * Default is {@code void}.
         *
         * @param consumer return type builder consumer
         * @return updated builder instance
         */
        public Builder returnType(Consumer consumer) {
            Objects.requireNonNull(consumer);
            Returns.Builder builder = Returns.builder();
            consumer.accept(builder);
            return returnType(builder);
        }

        /**
         * Set return type of the method.
         * Default is {@code void}.
         *
         * @param supplier return type supplier
         * @return updated builder instance
         */
        public Builder returnType(Supplier supplier) {
            Objects.requireNonNull(supplier);
            return returnType(supplier.get());
        }

        /**
         * Set return type of the method.
         * Default is {@code void}.
         *
         * @param returnType return type
         * @return updated builder instance
         */
        public Builder returnType(Returns returnType) {
            return type(returnType.type())
                    .returnJavadoc(returnType.description());
        }

        /**
         * Add generic argument to be declared by this method.
         *
         * @param typeArgument argument to be declared
         * @return updated builder instance
         */
        public Builder addGenericArgument(TypeArgument typeArgument) {
            declaredTokens.put(typeArgument.token(), typeArgument);
            addGenericToken(typeArgument.token(), typeArgument.description());
            return this;
        }

        Type returnType() {
            return type();
        }

    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy