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

io.rxmicro.annotation.processor.common.model.ClassHeader Maven / Gradle / Ivy

/*
 * Copyright (c) 2020. https://rxmicro.io
 *
 * 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.rxmicro.annotation.processor.common.model;

import io.rxmicro.annotation.processor.common.model.type.ObjectModelClass;
import io.rxmicro.annotation.processor.common.util.Names;
import io.rxmicro.common.InvalidStateException;
import io.rxmicro.common.meta.BuilderMethod;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.stream.Stream;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeMirror;

import static io.rxmicro.annotation.processor.common.util.Elements.asTypeElement;
import static io.rxmicro.annotation.processor.common.util.Names.getSimpleName;
import static io.rxmicro.annotation.processor.common.util.ProcessingEnvironmentHelper.getTypes;
import static io.rxmicro.common.util.Formats.format;
import static io.rxmicro.common.util.Requires.require;
import static java.lang.System.lineSeparator;
import static java.util.Map.entry;
import static java.util.stream.Collectors.toMap;

/**
 * @author nedis
 * @since 0.1
 */
public final class ClassHeader {

    private final String packageName;

    private final Map importsMap;

    private final Map staticImportsMap;

    public static Builder newClassHeaderBuilder(final String packageName) {
        return new Builder(packageName);
    }

    public static Builder newClassHeaderBuilder(final TypeElement typeElement) {
        return newClassHeaderBuilder(Names.getPackageName(typeElement));
    }

    public static Builder newClassHeaderBuilder(final ObjectModelClass objectModelClass) {
        return newClassHeaderBuilder(objectModelClass.getModelTypeElement());
    }

    private ClassHeader(final String packageName,
                        final Set imports,
                        final Set staticImports) {
        this.packageName = packageName;
        this.importsMap = imports.stream()
                .map(cl -> entry(getSimpleName(cl), cl))
                .collect(toMap(Map.Entry::getKey, Map.Entry::getValue, (v1, v2) -> {
                    throw new InvalidStateException(
                            "Detected different classes with the same simple name: '?' and '?'", v1, v2
                    );
                }));
        this.staticImportsMap = staticImports.stream()
                .map(cl -> entry(getSimpleName(cl), cl))
                .collect(toMap(Map.Entry::getKey, Map.Entry::getValue, (v1, v2) -> {
                    throw new InvalidStateException(
                            "Detected different static methods with the same simple name: '?' and '?'", v1, v2
                    );
                }));
    }

    public Map getEditableImports() {
        return importsMap;
    }

    public Map getEditableStaticImports() {
        return staticImportsMap;
    }

    public String buildHeader(final boolean withAuthorComment) {
        final Set javaImports = new TreeSet<>();
        final Set libImports = new TreeSet<>();
        final Set staticImports = new TreeSet<>(staticImportsMap.values());
        for (final String className : importsMap.values()) {
            final String packageName = Names.getPackageName(className);
            if (!this.packageName.equals(packageName) && !"java.lang".equals(packageName)) {
                if (packageName.startsWith("java.")) {
                    javaImports.add(className);
                } else {
                    libImports.add(className);
                }
            }
        }
        return buildHeader(withAuthorComment, javaImports, libImports, staticImports);
    }

    private String buildHeader(final boolean withAuthorComment,
                               final Set javaImports,
                               final Set libImports,
                               final Set staticImports) {
        final StringBuilder headerBuilder = new StringBuilder(50);
        // Add package
        headerBuilder
                .append("package ").append(packageName).append(';').append(lineSeparator())
                .append(lineSeparator());
        // Add imports
        for (final Set imports : Arrays.asList(libImports, javaImports, staticImports)) {
            final String startStatement = imports.equals(staticImports) ? "import static " : "import ";
            for (final String imp : imports) {
                headerBuilder.append(startStatement).append(imp).append(';').append(lineSeparator());
            }
            if (!imports.isEmpty()) {
                headerBuilder.append(lineSeparator());
            }
        }
        if (withAuthorComment) {
            headerBuilder.append(getAuthorComment());
        }
        return headerBuilder.toString();
    }

    public String getAuthorComment() {
        return "/**" + lineSeparator() +
                " * Generated by {@code RxMicro Annotation Processor}" + lineSeparator() +
                " */";
    }

    /**
     * @author nedis
     * @since 0.1
     */
    @SuppressWarnings("UnusedReturnValue")
    public static final class Builder {

        private static final TypeMirror[] EMPTY_TYPE_MIRROR_ARRAY = new TypeMirror[0];

        private final String packageName;

        private final Set imports = new HashSet<>();

        private final Set staticImports = new HashSet<>();

        private Builder(final String packageName) {
            this.packageName = require(packageName);
        }

        @BuilderMethod
        public Builder addImports(final TypeElement... types) {
            Arrays.stream(types).forEach(t -> addImport(t.getQualifiedName().toString()));
            return this;
        }

        @BuilderMethod
        public Builder addImports(final TypeMirror... types) {
            Arrays.stream(types)
                    .flatMap(this::expand)
                    .forEach(t -> asTypeElement(t)
                            .ifPresent(te -> addImport(t.toString())));
            return this;
        }

        @BuilderMethod
        public Builder addImports(final Class... classes) {
            Arrays.stream(classes).forEach(cl -> addImport(cl.getName()));
            return this;
        }

        @BuilderMethod
        public Builder addImports(final String... fullClassNames) {
            for (final String fullClassName : fullClassNames) {
                addImport(fullClassName);
            }
            return this;
        }

        @BuilderMethod
        public Builder addImports(final Collection fullClassNames) {
            addImports(fullClassNames.toArray(EMPTY_TYPE_MIRROR_ARRAY));
            return this;
        }

        private void addImport(final String fullClassName) {
            imports.add(fullClassName);
        }

        @BuilderMethod
        public Builder addStaticImport(final Class className,
                                       final String methodName) {
            addStaticImport(className.getName(), methodName);
            return this;
        }

        @BuilderMethod
        public Builder addStaticImport(final String className,
                                       final String methodName) {
            staticImports.add(format("?.?", className, methodName));
            return this;
        }

        private Stream expand(final TypeMirror type) {
            final List result = new ArrayList<>(2);
            populate(result, type);
            return result.stream();
        }

        private void populate(final List result,
                              final TypeMirror type) {
            if (type instanceof DeclaredType) {
                for (final TypeMirror typeArgument : ((DeclaredType) type).getTypeArguments()) {
                    populate(result, typeArgument);
                }
            }
            result.add(getTypes().erasure(type));
        }

        public ClassHeader build() {
            return new ClassHeader(packageName, imports, staticImports);
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy