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

com.atlassian.bamboo.specs.codegen.BambooSpecsGenerator Maven / Gradle / Ivy

There is a newer version: 10.1.0
Show newest version
package com.atlassian.bamboo.specs.codegen;

import com.atlassian.bamboo.specs.api.BambooSpec;
import com.atlassian.bamboo.specs.api.codegen.CodeEmitter;
import com.atlassian.bamboo.specs.api.codegen.CodeGenerationContext;
import com.atlassian.bamboo.specs.api.codegen.CodeGenerationException;
import com.atlassian.bamboo.specs.api.exceptions.PropertiesValidationException;
import com.atlassian.bamboo.specs.api.model.EntityProperties;
import com.atlassian.bamboo.specs.api.validators.common.ImporterUtils;
import com.atlassian.bamboo.specs.codegen.emitters.value.ValueEmitterFactory;
import com.atlassian.bamboo.specs.util.BambooServer;
import com.atlassian.bamboo.specs.util.BambooSpecProperties;
import com.atlassian.bamboo.specs.util.Yamlizator;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;

import javax.annotation.concurrent.Immutable;
import java.io.File;
import java.io.FileInputStream;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

/**
 * Code generator for any Bamboo Spec entities, such us plan or deployment project.
 */
public class BambooSpecsGenerator {
    private final Map entityByName = new LinkedHashMap<>();
    private final CodeGenerationContext context = new CodeGenerationContext();

    public interface CodeSupplier {
        String generateCode() throws CodeGenerationException;
    }

    public BambooSpecsGenerator() {
    }

    public BambooSpecsGenerator(final EntityProperties entityProperties) throws PropertiesValidationException {
        this();
        addEntity("rootObject", entityProperties);
    }


    public BambooSpecsGenerator addEntity(final String entityName, final EntityProperties entityProperties) throws PropertiesValidationException {
        ImporterUtils.checkNotNull("entity name", entityName);
        ImporterUtils.checkNotNull("entity", entityProperties);
        if (entityByName.putIfAbsent(entityName, entityProperties) != null) {
            throw new PropertiesValidationException(String.format("Entity with name %s already defined.", entityName));
        }
        return this;
    }

    private StringBuilder classDeclaration(@NotNull Set annotations, @NotNull String className, @NotNull CodeSupplier body) throws CodeGenerationException {
        final StringBuilder builder = new StringBuilder();
        //open class
        for (final Class annotation : annotations) {
            String annotationName = context.importClassName(annotation);
            builder.append("@").append(annotationName).append(context.newLine());
        }

        builder.append("public class ")
                .append(className)
                .append(" {");
        context.incIndentation();
        builder.append(body.generateCode());

        //close class
        context.decIndentation();
        builder.append(context.newLine());
        builder.append("}");
        context.decIndentation();
        builder.append(context.newLine());
        builder.append("}");

        return builder;
    }

    private String buildCode(String url, String className) throws CodeGenerationException {
        final String mainObjectName = getMainObjectName(className);

        final StringBuilder builder = classDeclaration(Collections.singleton(BambooSpec.class), className, () -> {
            final StringBuilder bodyBuilder = new StringBuilder();
            for (final String entityName : entityByName.keySet()) {
                insertCreationMethod(bodyBuilder, entityName, entityByName.get(entityName));
            }

            bodyBuilder.append(context.newLine());
            bodyBuilder.append(context.newLine());
            bodyBuilder.append("public static void main(String... argv) {");
            context.incIndentation();
            bodyBuilder.append(context.newLine());
            bodyBuilder.append("//By default credentials are read from the '.credentials' file.").append(context.newLine());
            bodyBuilder.append(context.importClassName(BambooServer.class))
                    .append(" bambooServer = new BambooServer(\"")
                    .append(url)
                    .append("\");");

            bodyBuilder.append(context.newLine());
            bodyBuilder.append("final ").append(className).append(" " + mainObjectName + " = new ").append(className).append("();");

            for (final String entityName : entityByName.keySet()) {
                bodyBuilder.append(context.newLine()).append(context.newLine());
                insertPublishingCode(bodyBuilder, mainObjectName, entityName, entityByName.get(entityName));
            }
            return bodyBuilder.toString();
        });
        return builder.toString();
    }

    private String getMainObjectName(String className) {
        String candidate = StringUtils.uncapitalize(className);
        if (!className.equals(candidate)) {
            return candidate;
        }
        return "mainObj";
    }


    private void insertCreationMethod(final StringBuilder builder,
                                      final String entityName,
                                      final EntityProperties entity) throws CodeGenerationException {

        if (!(entity instanceof Comment)) {
            builder.append(context.newLine()).append(context.newLine());
            final String builderClassName = context.importClassName(BuilderClassProvider.findBuilderClass(entity.getClass()));
            builder.append("public ")
                    .append(builderClassName)
                    .append(" ")
                    .append(entityName)
                    .append("() {");

            context.incIndentation();
            builder.append(context.newLine());
            final CodeEmitter codeEmitter = ValueEmitterFactory.emitterFor(entity);

            final String entityCodeRightSide = codeEmitter.emitCode(context, entity);
            builder.append("final ").append(builderClassName);
            builder.append(String.format(" %s = %s;", entityName, entityCodeRightSide));
            builder.append(context.newLine());
            builder.append("return ").append(entityName).append(";");

            context.decIndentation();
            builder.append(context.newLine());
            builder.append("}");
        }
    }

    private void insertPublishingCode(final StringBuilder builder,
                                      final String classVariable,
                                      final String entityName,
                                      final EntityProperties entity) throws CodeGenerationException {

        if (entity instanceof Comment) {
            final Comment comment = (Comment) entity;
            if (comment.newLineBefore) {
                builder.append(context.newLine());
            }
            builder.append("// ").append(comment.comment);
            if (comment.newLineAfter) {
                builder.append(context.newLine());
            }
        } else {
            final CodeEmitter codeEmitter = ValueEmitterFactory.emitterFor(entity);

            final String entityCodeRightSide = classVariable + "." + entityName + "()";
            builder.append("final ").append(context.importClassName(BuilderClassProvider.findBuilderClass(entity.getClass())));
            builder.append(String.format(" %s = %s;", entityName, entityCodeRightSide));
            builder.append(context.newLine());

            builder.append(String.format("bambooServer.publish(%s);", entityName));
        }

    }

    public String emitCode() throws CodeGenerationException {
        return emitCode("http://localhost:8085");
    }

    public String emitCode(@NotNull String bambooServerUrl) throws CodeGenerationException {
        return emitCode(bambooServerUrl, "", "PlanSpec");
    }

    public String emitCode(@NotNull String bambooServerUrl, @NotNull String packageName, @NotNull String className) throws CodeGenerationException {
        final String main = buildCode(bambooServerUrl, className);

        final StringBuilder codeBuilder = new StringBuilder();
        //imports
        if (StringUtils.isNotBlank(packageName)) {
            codeBuilder.append("package ").append(packageName).append(";\n\n");
        }
        for (String anImport : context.getImports()) {
            codeBuilder.append("import ").append(anImport).append(";\n");
        }
        codeBuilder.append("\n");
        codeBuilder.append(main);
        return codeBuilder.toString();
    }

    /**
     * Represents a comment in Bamboo Specs.
     * Note: Only to be used with {@link BambooSpecsGenerator#addEntity(String, EntityProperties)}
     */
    @Immutable
    public static class Comment implements EntityProperties {

        private String comment;
        private boolean newLineBefore;
        private boolean newLineAfter;

        private Comment() {
        }

        public Comment(final String comment) {
            this(comment, true, true);
        }

        public Comment(final String comment, final boolean newLineBefore, final boolean newLineAfter) {
            this.comment = comment;
            this.newLineBefore = newLineBefore;
            this.newLineAfter = newLineAfter;
        }

        @Override
        public String toString() {
            return comment;
        }

        @Override
        public void validate() {
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            Comment comment1 = (Comment) o;
            return newLineBefore == comment1.newLineBefore &&
                    newLineAfter == comment1.newLineAfter &&
                    Objects.equals(comment, comment1.comment);
        }

        @Override
        public int hashCode() {
            return Objects.hash(comment, newLineBefore, newLineAfter);
        }
    }

    public static void main(String... argv) {
        if (argv.length < 1) {
            System.out.println("File argument missing");
            return;
        }
        String filePath = argv[0];
        try {
            BambooSpecProperties o = (BambooSpecProperties) Yamlizator.getYaml().load(new FileInputStream(new File(filePath)));

            String javaCode = new BambooSpecsGenerator(o.getRootEntity()).emitCode();
            System.out.print(javaCode);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy