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

de.dentrassi.asyncapi.generator.java.Generator Maven / Gradle / Ivy

/*
 * Copyright (C) 2017 Jens Reimann 
 *
 * 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 de.dentrassi.asyncapi.generator.java;

import static de.dentrassi.asyncapi.generator.java.PackageTypeBuilder.asPropertyName;
import static de.dentrassi.asyncapi.generator.java.PackageTypeBuilder.asTypeName;
import static de.dentrassi.asyncapi.generator.java.util.JDTHelper.makeProtected;
import static de.dentrassi.asyncapi.generator.java.util.JDTHelper.makePublic;
import static de.dentrassi.asyncapi.generator.java.util.JDTHelper.newStringLiteral;
import static de.dentrassi.asyncapi.generator.java.util.Names.makeVersion;

import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.eclipse.jdt.core.dom.AST;
import org.eclipse.jdt.core.dom.Block;
import org.eclipse.jdt.core.dom.Javadoc;
import org.eclipse.jdt.core.dom.MemberValuePair;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.MethodInvocation;
import org.eclipse.jdt.core.dom.Modifier.ModifierKeyword;
import org.eclipse.jdt.core.dom.NormalAnnotation;
import org.eclipse.jdt.core.dom.ParameterizedType;
import org.eclipse.jdt.core.dom.ReturnStatement;
import org.eclipse.jdt.core.dom.SimpleType;
import org.eclipse.jdt.core.dom.StringLiteral;
import org.eclipse.jdt.core.dom.TagElement;
import org.eclipse.jdt.core.dom.TypeDeclaration;
import org.eclipse.jdt.core.dom.TypeLiteral;
import org.eclipse.jdt.core.dom.TypeParameter;

import de.dentrassi.asyncapi.ArrayType;
import de.dentrassi.asyncapi.AsyncApi;
import de.dentrassi.asyncapi.CoreType;
import de.dentrassi.asyncapi.EnumType;
import de.dentrassi.asyncapi.Information;
import de.dentrassi.asyncapi.Message;
import de.dentrassi.asyncapi.MessageReference;
import de.dentrassi.asyncapi.ObjectType;
import de.dentrassi.asyncapi.ParentableType;
import de.dentrassi.asyncapi.Property;
import de.dentrassi.asyncapi.Topic;
import de.dentrassi.asyncapi.Type;
import de.dentrassi.asyncapi.TypeReference;
import de.dentrassi.asyncapi.generator.java.ServiceDefinitions.VersionedService;
import de.dentrassi.asyncapi.generator.java.util.JDTHelper;

public class Generator {

    private static final String TYPE_NAME_CONNECTOR = "de.dentrassi.asyncapi.Connector";

    public static final class Options {
        private Path targetPath;
        private Charset characterSet = StandardCharsets.UTF_8;
        private String basePackage;

        private Options() {
        }

        private Options(final Options other) {
            this.targetPath = other.targetPath;
            this.characterSet = other.characterSet;
            this.basePackage = other.basePackage;
        }

        public String getBasePackage() {
            return this.basePackage;
        }

        public Charset getCharacterSet() {
            return this.characterSet;
        }

        public Path getTargetPath() {
            return this.targetPath;
        }

        private void validate(final List errors) {
            if (this.targetPath == null) {
                errors.add(new IllegalStateException("'targetPath' is not set"));
            }
        }
    }

    public static final class Builder {

        private final Options options = new Options();

        private boolean validateTopicSyntax = true;

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

        private Builder() {
        }

        public void addExtension(final GeneratorExtension extension) {
            Objects.requireNonNull(extension);

            this.extensions.add(extension);
        }

        public Builder validateTopicSyntax(final boolean validateTopicSyntax) {
            this.validateTopicSyntax = validateTopicSyntax;
            return this;
        }

        public Builder targetPath(final Path targetPath) {
            Objects.requireNonNull(targetPath);

            this.options.targetPath = targetPath;
            return this;
        }

        public Builder basePackage(final String basePackage) {
            this.options.basePackage = basePackage;
            return this;
        }

        public Builder characterSet(final Charset characterSet) {
            this.options.characterSet = characterSet;
            return this;
        }

        public Generator build(final AsyncApi api) {

            final LinkedList errors = new LinkedList<>();
            this.options.validate(errors);

            if (!errors.isEmpty()) {
                final RuntimeException e = new RuntimeException("Invalid generator settings", errors.pollFirst());
                errors.stream().forEach(e::addSuppressed);
                throw e;
            }

            return new Generator(api, new Options(this.options), this.validateTopicSyntax, new ArrayList<>(this.extensions));
        }
    }

    public interface Context {
        public TypeBuilder createTypeBuilder(final String localPackageName);

        public String fullQualifiedName(String... localName);

        public ServiceDefinitions getServiceDefinitions();
    }

    private static final String TYPE_NAME_MESSAGE_INTERFACE = "de.dentrassi.asyncapi.Message";

    private static final String TYPE_NAME_PUBSUB_CLASS = "de.dentrassi.asyncapi.PublishSubscribe";

    private static final String TYPE_NAME_SUB_CLASS = "de.dentrassi.asyncapi.Subscribe";

    private static final String TYPE_NAME_PUB_CLASS = "de.dentrassi.asyncapi.Publish";

    private static final String TYPE_NAME_TOPIC_ANN = "de.dentrassi.asyncapi.Topic";

    private static final String TYPE_NAME_ABSTRACT_CONNECTOR_BUILDER = "de.dentrassi.asyncapi.Connector.AbstractBuilder";

    public static Builder newBuilder() {
        return new Builder();
    }

    private final AsyncApi api;
    private final boolean validateTopicSyntax;
    private List extensions = new ArrayList<>();

    private final Options options;

    private final Context context = new Context() {
        @Override
        public TypeBuilder createTypeBuilder(final String localPackageName) {
            return Generator.this.createTypeBuilder(localPackageName);
        }

        @Override
        public String fullQualifiedName(final String... localName) {
            return packageName(localName);
        }

        @Override
        public ServiceDefinitions getServiceDefinitions() {
            return Generator.this.serviceDefinitions;
        }
    };

    private final ServiceDefinitions serviceDefinitions;

    private Generator(final AsyncApi api, final Options options, final boolean validateTopicSyntax, final List extensions) {
        this.api = api;
        this.options = options;
        this.validateTopicSyntax = validateTopicSyntax;
        this.extensions = extensions;

        this.serviceDefinitions = ServiceDefinitions.build(this.api, this.validateTopicSyntax);
    }

    public void generate() throws IOException {
        Files.createDirectories(this.options.getTargetPath());

        generateRoot();
        generateMessages();
        generateTypes();
        generateTopics();

        for (final GeneratorExtension extension : this.extensions) {
            extension.generate(this.api, this.options, this.context);
        }
    }

    private void generateTopics() {
        renderServices(ConnectorType.CLIENT);
        renderServices(ConnectorType.SERVER);
        renderConnector(ConnectorType.CLIENT);
        renderConnector(ConnectorType.SERVER);
    }

    private TypeBuilder createTypeBuilder(final String... localPackageName) {
        return new PackageTypeBuilder(this.options.getTargetPath(), packageName(localPackageName), this.options.getCharacterSet(), type -> null, this::lookupType);
    }

    private void renderConnector(final ConnectorType connectorType) {

        final TypeBuilder builder = createTypeBuilder();

        final Consumer typeCustomizer = TypeBuilder.asInterface(true) //
                .andThen(TypeBuilder.superInterfaces(Arrays.asList(TYPE_NAME_CONNECTOR)));

        builder.createType(new TypeInformation(connectorType.getSimpleTypeName(), null, null), typeCustomizer, b -> {

            renderDefaultConnectorBuilder(b, connectorType);

            for (final Map.Entry>> versionEntry : this.serviceDefinitions.getVersions().entrySet()) {
                final String version = makeVersion(versionEntry.getKey());

                b.createType(new TypeInformation(version.toUpperCase(), null, null), true, false, vb -> {

                    for (final Map.Entry> serviceEntry : versionEntry.getValue().entrySet()) {

                        final TypeInformation serviceType = createServiceTypeInformation(serviceEntry);

                        vb.createMethod((ast, cu) -> {
                            final MethodDeclaration md = ast.newMethodDeclaration();
                            md.setName(ast.newSimpleName(PackageTypeBuilder.asPropertyName(serviceType.getName())));
                            md.setReturnType2(ast.newSimpleType(ast.newName(packageName(connectorType.getPackageName(), version, serviceType.getName()))));
                            return md;
                        });

                    }

                });

                // create version method - e.g. v1()

                b.createMethod((ast, cu) -> {
                    final MethodDeclaration md = ast.newMethodDeclaration();
                    md.setName(ast.newSimpleName(version));
                    md.setReturnType2(ast.newSimpleType(ast.newSimpleName(version.toUpperCase())));
                    return md;
                });

            }

            // create latest versions

            for (final Map.Entry latestEntry : this.serviceDefinitions.getLatest().entrySet()) {
                b.createMethod((ast, cu) -> {
                    return createReturnLatestVersionService(latestEntry, ast, connectorType);
                });
            }

        });

    }

    @SuppressWarnings("unchecked")
    private static ParameterizedType parametrizeSimple(final org.eclipse.jdt.core.dom.Type original, final String... parameters) {
        final AST ast = original.getAST();

        final ParameterizedType result = ast.newParameterizedType(original);

        for (final String name : parameters) {
            final SimpleType type = ast.newSimpleType(ast.newSimpleName(name));
            result.typeArguments().add(type);
        }

        return result;
    }

    @SuppressWarnings("unchecked")
    private void renderDefaultConnectorBuilder(final TypeBuilder builder, final ConnectorType connectorType) {

        Consumer typeCustomizer = td -> {
            final AST ast = td.getAST();

            final SimpleType st = ast.newSimpleType(ast.newName(TYPE_NAME_ABSTRACT_CONNECTOR_BUILDER));

            td.setSuperclassType(parametrizeSimple(st, "B", "C"));
        };

        typeCustomizer = typeCustomizer.andThen(td -> {
            final AST ast = td.getAST();

            final TypeParameter b = ast.newTypeParameter();
            b.setName(ast.newSimpleName("B"));
            b.typeBounds().add(parametrizeSimple(ast.newSimpleType(ast.newSimpleName("Builder")), "B", "C"));

            final TypeParameter c = ast.newTypeParameter();
            c.setName(ast.newSimpleName("C"));
            c.typeBounds().add(ast.newSimpleType(ast.newSimpleName(connectorType.getSimpleTypeName())));

            td.typeParameters().add(b);
            td.typeParameters().add(c);
        });

        typeCustomizer = typeCustomizer.andThen(td -> JDTHelper.make(td, ModifierKeyword.STATIC_KEYWORD, ModifierKeyword.ABSTRACT_KEYWORD));

        builder.createType(new TypeInformation("Builder", null, null), typeCustomizer, b -> {

            b.createMethod((ast, cu) -> {
                final MethodDeclaration md = ast.newMethodDeclaration();
                md.setConstructor(true);
                md.setName(ast.newSimpleName("Builder"));
                makeProtected(md);

                final Block body = ast.newBlock();
                md.setBody(body);

                if (this.api.getHost() != null && !this.api.getHost().isEmpty()) {
                    final MethodInvocation mi = ast.newMethodInvocation();
                    mi.setName(ast.newSimpleName("host"));
                    mi.arguments().add(newStringLiteral(ast, this.api.getHost()));
                    body.statements().add(ast.newExpressionStatement(mi));
                }

                if (this.api.getBaseTopic() != null && !this.api.getBaseTopic().isEmpty()) {
                    final MethodInvocation mi = ast.newMethodInvocation();
                    mi.setName(ast.newSimpleName("baseTopic"));
                    mi.arguments().add(newStringLiteral(ast, this.api.getBaseTopic()));
                    body.statements().add(ast.newExpressionStatement(mi));
                }

                return md;
            });

        });
    }

    @SuppressWarnings("unchecked")
    private MethodDeclaration createReturnLatestVersionService(final Map.Entry latestEntry, final AST ast, final ConnectorType connectorType) {
        final String version = makeVersion(latestEntry.getValue().getVersion().toString());
        final String serviceType = packageName(connectorType.getPackageName(), version, latestEntry.getValue().getType().getName());

        final MethodDeclaration md = ast.newMethodDeclaration();

        md.setName(ast.newSimpleName(asPropertyName(latestEntry.getKey())));
        md.setReturnType2(ast.newSimpleType(ast.newName(serviceType)));

        md.modifiers().add(ast.newModifier(ModifierKeyword.DEFAULT_KEYWORD));

        final Block block = ast.newBlock();
        md.setBody(block);

        final ReturnStatement ret = ast.newReturnStatement();
        block.statements().add(ret);

        final MethodInvocation versionMethod = ast.newMethodInvocation();
        versionMethod.setName(ast.newSimpleName(version.toLowerCase()));

        final MethodInvocation serviceMethod = ast.newMethodInvocation();
        serviceMethod.setName(ast.newSimpleName(asPropertyName(latestEntry.getKey())));
        serviceMethod.setExpression(versionMethod);

        ret.setExpression(serviceMethod);

        return md;
    }

    @SuppressWarnings("unchecked")
    private void renderServices(final ConnectorType connectorType) {
        for (final Map.Entry>> versionEntry : this.serviceDefinitions.getVersions().entrySet()) {

            final String packageName = connectorType.getPackageName();
            final String version = makeVersion(versionEntry.getKey());
            final TypeBuilder builder = new PackageTypeBuilder(this.options.getTargetPath(), packageName(packageName, version), this.options.getCharacterSet(), type -> null,
                    this::lookupType);

            for (final Map.Entry> serviceEntry : versionEntry.getValue().entrySet()) {
                builder.createType(createServiceTypeInformation(serviceEntry), true, false, b -> {

                    for (final Topic topic : serviceEntry.getValue()) {
                        b.createMethod((ast, cu) -> {

                            final TopicInformation ti = this.serviceDefinitions.getTopics().get(topic);

                            // new method

                            final MethodDeclaration md = ast.newMethodDeclaration();
                            md.setName(ast.newSimpleName(makeTopicMethodName(ti)));

                            // return type

                            // set return type

                            md.setReturnType2(evalEventMethodType(ast, topic, this.context, connectorType));

                            // assign annotation

                            final NormalAnnotation an = ast.newNormalAnnotation();
                            an.setTypeName(ast.newName(TYPE_NAME_TOPIC_ANN));
                            an.values().add(newKeyValueString(ast, "name", topic.getName()));
                            if (topic.getPublish() != null) {
                                an.values().add(newKeyValueClass(ast, "publish", messageTypeName(topic.getPublish(), this.context)));
                            }
                            if (topic.getSubscribe() != null) {
                                an.values().add(newKeyValueClass(ast, "subscribe", messageTypeName(topic.getSubscribe(), this.context)));
                            }
                            md.modifiers().add(an);

                            // make public

                            makePublic(md);

                            // return

                            return md;

                        });

                    }

                });
            }

        }
    }

    public static TypeInformation createServiceTypeInformation(final Map.Entry> serviceEntry) {
        return new TypeInformation(asTypeName(serviceEntry.getKey()), null, null);
    }

    @SuppressWarnings("unchecked")
    public static ParameterizedType evalEventMethodType(final AST ast, final Topic topic, final Context context, final ConnectorType connectorType) {

        final MessageReference pubMsg = connectorType.getPublish(topic);
        final MessageReference subMsg = connectorType.getSubscribe(topic);

        if (pubMsg == null && subMsg == null) {
            return null;
        }

        final SimpleType eventType;

        if (pubMsg != null && subMsg != null) {
            eventType = ast.newSimpleType(ast.newName(TYPE_NAME_PUBSUB_CLASS));
        } else if (pubMsg != null) {
            eventType = ast.newSimpleType(ast.newName(TYPE_NAME_PUB_CLASS));
        } else {
            eventType = ast.newSimpleType(ast.newName(TYPE_NAME_SUB_CLASS));
        }

        final ParameterizedType type = ast.newParameterizedType(eventType);

        if (pubMsg != null) {
            type.typeArguments().add(ast.newSimpleType(ast.newName(messageTypeName(pubMsg, context))));
        }
        if (subMsg != null) {
            type.typeArguments().add(ast.newSimpleType(ast.newName(messageTypeName(subMsg, context))));
        }

        return type;
    }

    private static MemberValuePair newKeyValueClass(final AST ast, final String key, final String typeName) {

        final MemberValuePair pair = ast.newMemberValuePair();

        pair.setName(ast.newSimpleName(key));

        final TypeLiteral type = ast.newTypeLiteral();
        type.setType(ast.newSimpleType(ast.newName(typeName)));

        pair.setValue(type);

        return pair;
    }

    private static MemberValuePair newKeyValueString(final AST ast, final String key, final String value) {

        final MemberValuePair pair = ast.newMemberValuePair();

        pair.setName(ast.newSimpleName(key));

        final StringLiteral topicLiteral = ast.newStringLiteral();
        topicLiteral.setLiteralValue(value);

        pair.setValue(topicLiteral);

        return pair;
    }

    public static String messageTypeName(final MessageReference message, final Context context) {
        return context.fullQualifiedName("messages") + "." + PackageTypeBuilder.asTypeName(message.getName());
    }

    public static String makeTopicMethodName(final TopicInformation ti) {

        Stream s = Stream.of(ti.getType());

        s = Stream.concat(s, ti.getResources().stream());
        s = Stream.concat(s, Stream.of(ti.getAction()));

        if (ti.getStatus().isPresent()) {
            s = Stream.concat(s, Stream.of(ti.getStatus().get().toString().toLowerCase()));
        }

        return joinLowerCamelCase(s);
    }

    private static String joinLowerCamelCase(final Stream s) {

        boolean first = true;

        final StringBuilder sb = new StringBuilder();

        for (final String tok : (Iterable) s::iterator) {
            final int len = tok.length();

            if (len == 0) {
                continue;
            }

            final Function fn = first ? Character::toLowerCase : Character::toUpperCase;
            first = false;

            sb.append(fn.apply(tok.charAt(0)));
            sb.append(tok.substring(1).toLowerCase());
        }

        return sb.toString();
    }

    private void generateMessages() {
        final TypeBuilder builder = new PackageTypeBuilder(this.options.getTargetPath(), packageName("messages"), this.options.getCharacterSet(), this::resolveTypeName,
                this::lookupType);

        this.api.getMessages().forEach(message -> {
            generateMessage(builder, message);
        });
    }

    private Type lookupType(final String typeName) {
        return this.api.getTypes().stream().filter(type -> type.getName().equals(typeName)).findFirst()
                .orElseThrow(() -> new IllegalStateException(String.format("Unknown type '%s' referenced", typeName)));
    }

    private void generateMessage(final TypeBuilder builder, final Message message) {

        final TypeInformation ti = new TypeInformation(asTypeName(message.getName()), message.getSummary(), message.getDescription());

        //         final String payloadTypeName = PackageTypeBuilder.asTypeName(message.getPayload().getName());

        @SuppressWarnings("unchecked")
        final Consumer typeCustomizer = td -> {
            final AST ast = td.getAST();

            final ParameterizedType type = ast.newParameterizedType(ast.newSimpleType(ast.newName(TYPE_NAME_MESSAGE_INTERFACE)));
            type.typeArguments().add(ast.newSimpleType(ast.newName(ti.getName() + ".Payload")));

            td.superInterfaceTypes().add(type);
        };

        final TypeReference payloadType = message.getPayload();

        builder.createType(ti, typeCustomizer, b -> {

            if (payloadType instanceof ObjectType) {

                generateType(b, (Type) payloadType);

                b.createProperty(new PropertyInformation((Type) payloadType, "payload", "Message payload", null));

            } else if (payloadType instanceof CoreType) {

                b.createProperty(new PropertyInformation((CoreType) message.getPayload(), "payload", "Message payload", null));

            } else if (payloadType.getClass().equals(TypeReference.class)) {
                b.createProperty(new PropertyInformation(lookupType(payloadType), "payload", "Message payload", null));
            } else {
                throw new IllegalStateException("Unsupported payload type: " + message.getPayload().getClass().getName());
            }

        });
    }

    private void generateTypes() {

        final TypeBuilder builder = new PackageTypeBuilder(this.options.getTargetPath(), packageName("types"), this.options.getCharacterSet(), this::resolveTypeName,
                this::lookupType);

        this.api.getTypes().forEach(type -> {
            generateType(builder, type);
        });
    }

    private void generateType(final TypeBuilder builder, final Type type) {
        if (type instanceof EnumType) {
            generateEnum((EnumType) type, builder);
        } else if (type instanceof ObjectType) {
            generateObject((ObjectType) type, builder);
        }
    }

    private void generateObject(final ObjectType type, final TypeBuilder builder) {

        final TypeInformation ti = new TypeInformation(asTypeName(type.getName()), type.getTitle(), type.getDescription());

        builder.createType(ti, false, true, b -> {

            for (final Property property : type.getProperties()) {
                generateProperty(property, b);
            }
        });

    }

    private static String toPrimitives(final CoreType type) {
        final String typeName = type.getJavaType().getName();

        switch (typeName) {
        case "java.lang.Boolean":
            return "boolean";
        case "java.lang.Integer":
            return "int";
        case "java.lang.Long":
            return "long";
        case "java.lang.Float":
            return "float";
        case "java.lang.Short":
            return "short";
        case "java.lang.Byte":
            return "byte";
        case "java.lang.Character":
            return "char";
        case "java.lang.Double":
            return "double";
        default:
            return typeName;
        }

    }

    private String resolveTypeName(final Type type) {
        return resolveTypeName(type, false);
    }

    private String resolveTypeName(final TypeReference typeRef, final boolean allowPrimitives) {
        if (typeRef instanceof ObjectType || typeRef instanceof EnumType) {

            return resolveParentableTypeName(typeRef);

        } else if (typeRef instanceof CoreType) {

            if (allowPrimitives) {
                return toPrimitives((CoreType) typeRef);
            } else {
                return ((CoreType) typeRef).getJavaType().getName();
            }

        } else if (typeRef instanceof ArrayType) {

            return resolveTypeName(((ArrayType) typeRef).getItemType(), false);

        } else {

            return resolveTypeName(lookupType(typeRef.getName()), allowPrimitives);

        }
    }

    private String resolveParentableTypeName(final TypeReference typeRef) {
        final List full = new LinkedList<>();

        full.add(typeRef.getNamespace());

        if (typeRef instanceof ParentableType) {
            for (final String parent : ((ParentableType) typeRef).getParents()) {
                full.add(asTypeName(parent));
            }
        }

        full.add(asTypeName(typeRef.getName()));

        return packageName(String.join(".", full));
    }

    private Type lookupType(final TypeReference typeRef) {
        if (typeRef instanceof Type) {
            return (Type) typeRef;
        }

        return lookupType(typeRef.getName());
    }

    private void generateProperty(final Property property, final TypeBuilder builder) {

        // build local type

        final TypeReference type = property.getType();
        if (type instanceof Type) {
            generateType(builder, (Type) type);
        }

        // generate property

        final String name = PackageTypeBuilder.asPropertyName(property.getName());

        final String summary = property.getDescription(); // FIXME: chase description
        final String description = null; // FIXME: chase description

        builder.createProperty(new PropertyInformation(lookupType(type), name, summary, description));
    }

    private void generateEnum(final EnumType type, final TypeBuilder builder) {
        builder.createEnum(new TypeInformation(asTypeName(type.getName()), type.getTitle(), type.getDescription()), type.getLiterals(),
                (literal, decl) -> fireExtensions(extension -> extension.createdEnumLiteral(literal, decl)), true);
    }

    protected  void fireExtensions(final Consumer consumer) {
        for (final GeneratorExtension extension : this.extensions) {
            consumer.accept(extension);
        }
    }

    protected  void fireExtensions(final T literal, final BiConsumer consumer) {
        for (final GeneratorExtension extension : this.extensions) {
            consumer.accept(extension, literal);
        }
    }

    private String packageName(final String... local) {

        Stream full;

        if (this.options.getBasePackage() != null && !this.options.getBasePackage().isEmpty()) {
            full = Stream.of(this.options.getBasePackage());
        } else if (this.api.getBaseTopic() != null && !this.api.getBaseTopic().isEmpty()) {
            full = Stream.of(this.api.getBaseTopic());
        } else {
            full = Stream.empty();
        }

        if (local != null) {
            full = Stream.concat(full, Arrays.stream(local));
        }

        return full.collect(Collectors.joining("."));
    }

    @SuppressWarnings("unchecked")
    private void generateRoot() throws IOException {

        PackageTypeBuilder.createCompilationUnit(this.options.getTargetPath(), packageName(), "package-info", this.options.getCharacterSet(), (ast, cu) -> {
            final Information info = this.api.getInformation();

            final Javadoc doc = ast.newJavadoc();

            if (info.getTitle() != null) {
                final TagElement tag = ast.newTagElement();
                tag.fragments().add(JDTHelper.newText(ast, info.getTitle()));
                doc.tags().add(tag);
            }

            if (info.getVersion() != null) {
                final TagElement version = ast.newTagElement();
                version.setTagName("@version");
                version.fragments().add(JDTHelper.newText(ast, info.getVersion()));
                doc.tags().add(version);
            }

            cu.getPackage().setJavadoc(doc);

        });
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy