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

software.amazon.smithy.traitcodegen.generators.ConstructorGenerator Maven / Gradle / Ivy

/*
 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
 * SPDX-License-Identifier: Apache-2.0
 */

package software.amazon.smithy.traitcodegen.generators;

import software.amazon.smithy.codegen.core.Symbol;
import software.amazon.smithy.codegen.core.SymbolProvider;
import software.amazon.smithy.model.FromSourceLocation;
import software.amazon.smithy.model.SourceLocation;
import software.amazon.smithy.model.node.Node;
import software.amazon.smithy.model.shapes.BigDecimalShape;
import software.amazon.smithy.model.shapes.BigIntegerShape;
import software.amazon.smithy.model.shapes.BlobShape;
import software.amazon.smithy.model.shapes.BooleanShape;
import software.amazon.smithy.model.shapes.ByteShape;
import software.amazon.smithy.model.shapes.DocumentShape;
import software.amazon.smithy.model.shapes.DoubleShape;
import software.amazon.smithy.model.shapes.EnumShape;
import software.amazon.smithy.model.shapes.FloatShape;
import software.amazon.smithy.model.shapes.IntEnumShape;
import software.amazon.smithy.model.shapes.IntegerShape;
import software.amazon.smithy.model.shapes.ListShape;
import software.amazon.smithy.model.shapes.LongShape;
import software.amazon.smithy.model.shapes.MapShape;
import software.amazon.smithy.model.shapes.MemberShape;
import software.amazon.smithy.model.shapes.NumberShape;
import software.amazon.smithy.model.shapes.Shape;
import software.amazon.smithy.model.shapes.ShapeVisitor;
import software.amazon.smithy.model.shapes.ShortShape;
import software.amazon.smithy.model.shapes.StringShape;
import software.amazon.smithy.model.shapes.StructureShape;
import software.amazon.smithy.model.shapes.TimestampShape;
import software.amazon.smithy.model.shapes.UnionShape;
import software.amazon.smithy.model.traits.TraitDefinition;
import software.amazon.smithy.model.traits.UniqueItemsTrait;
import software.amazon.smithy.traitcodegen.SymbolProperties;
import software.amazon.smithy.traitcodegen.TraitCodegenUtils;
import software.amazon.smithy.traitcodegen.writer.TraitCodegenWriter;
import software.amazon.smithy.utils.SmithyBuilder;

/**
 * Generates a constructor for a type.
 */
final class ConstructorGenerator extends TraitVisitor implements Runnable {
    private final TraitCodegenWriter writer;
    private final Symbol symbol;
    private final Shape shape;
    private final SymbolProvider symbolProvider;

    ConstructorGenerator(TraitCodegenWriter writer,
                         Symbol symbol,
                         Shape shape,
                         SymbolProvider symbolProvider
    ) {
        this.writer = writer;
        this.symbol = symbol;
        this.shape = shape;
        this.symbolProvider = symbolProvider;
    }

    @Override
    public void run() {
        shape.accept(this);
    }

    @Override
    public Void listShape(ListShape shape) {
        if (!shape.hasTrait(UniqueItemsTrait.class)
                && TraitCodegenUtils.isJavaString(symbolProvider.toSymbol(shape.getMember()))
        ) {
            writer.openBlock("public $1T($1B values, $2T sourceLocation) {", "}",
                    symbol, FromSourceLocation.class, () -> writer.write("super(ID, values, sourceLocation);"));
            writer.newLine();

            writer.openBlock("public $1T($1B values) {", "}", symbol,
                    () -> writer.write("super(ID, values, $T.NONE);", SourceLocation.class));
            writer.newLine();
        } else {
            writeConstructorWithBuilder();
        }

        return null;
    }

    @Override
    public Void mapShape(MapShape shape) {
        writeConstructorWithBuilder();
        return null;
    }

    @Override
    public Void intEnumShape(IntEnumShape shape) {
        Symbol integerSymbol = TraitCodegenUtils.fromClass(Integer.class);
        // Constructor with no source location
        writer.openBlock("public $T($T value) {", "}",
                symbol, integerSymbol, () -> {
            writer.write("super(ID, $T.NONE);", SourceLocation.class);
            writer.writeWithNoFormatting("this.value = value;");
        });
        writer.newLine();

        // Constructor with source location
        writer.openBlock("public $T($T value, $T sourceLocation) {", "}",
                symbol, integerSymbol, FromSourceLocation.class, () -> {
                    writer.writeWithNoFormatting("super(ID, sourceLocation);");
                    writer.writeWithNoFormatting("this.value = value;");
                });
        writer.newLine();
        return null;
    }

    @Override
    public Void documentShape(DocumentShape shape) {
        writer.openBlock("public $T($T value) {", "}",
                symbol, Node.class, () -> writer.writeWithNoFormatting("super(ID, value);"));
        writer.newLine();
        return null;
    }

    @Override
    public Void stringShape(StringShape shape) {
        if (TraitCodegenUtils.isJavaString(symbol)) {
            writeStringTraitConstructors();
        } else {
            writeValueShapeConstructors();
        }
        return null;
    }

    @Override
    public Void enumShape(EnumShape shape) {
        writeStringTraitConstructors();
        return null;
    }

    @Override
    public Void structureShape(StructureShape shape) {
        writeConstructorWithBuilder();
        return null;
    }

    @Override
    public Void timestampShape(TimestampShape shape) {
        writeValueShapeConstructors();
        return null;
    }

    @Override
    protected Void numberShape(NumberShape shape) {
        writeValueShapeConstructors();
        return null;
    }

    private void writeConstructorWithBuilder() {
        writer.openBlock("private $T(Builder builder) {", "}", symbol, () -> {
            // If the shape is a trait include the source location. Nested shapes don't have a separate source location.
            if (shape.hasTrait(TraitDefinition.class)) {
                writer.writeWithNoFormatting("super(ID, builder.getSourceLocation());");
            }
            shape.accept(new InitializerVisitor());
        });
        writer.newLine();
    }

    private void writeValueShapeConstructors() {
        // Constructor with no source location
        writer.openBlock("public $1T($1B value) {", "}", symbol, () -> {
            writer.write("super(ID, $T.NONE);", SourceLocation.class);
            writer.writeWithNoFormatting("this.value = value;");
        });
        writer.newLine();

        // Constructor with source location
        writer.openBlock("public $1T($1B value, $2T sourceLocation) {", "}",
                symbol, FromSourceLocation.class, () -> {
                    writer.writeWithNoFormatting("super(ID, sourceLocation);");
                    writer.writeWithNoFormatting("this.value = value;");
                });
        writer.newLine();
    }

    private void writeStringTraitConstructors() {
        // Without source location
        writer.openBlock("public $T(String value) {", "}", symbol,
                () -> writer.write("super(ID, value, $T.NONE);", SourceLocation.class));
        writer.newLine();

        // With source location
        writer.openBlock("public $T($T value, $T sourceLocation) {", "}",
                symbol, String.class, FromSourceLocation.class,
                () -> writer.writeWithNoFormatting("super(ID, value, sourceLocation);"));
        writer.newLine();
    }

    /**
     * Generates the actual field initialization statements for each member of a shape.
     */
    private final class InitializerVisitor extends ShapeVisitor.DataShapeVisitor {

        @Override
        public Void booleanShape(BooleanShape shape) {
            return null;
        }

        @Override
        public Void listShape(ListShape shape) {
            writeValuesInitializer();
            return null;
        }

        @Override
        public Void mapShape(MapShape shape) {
            writeValuesInitializer();
            return null;
        }

        @Override
        public Void byteShape(ByteShape shape) {
            return null;
        }

        @Override
        public Void shortShape(ShortShape shape) {
            return null;
        }

        @Override
        public Void integerShape(IntegerShape shape) {
            return null;
        }

        @Override
        public Void longShape(LongShape shape) {
            return null;
        }

        @Override
        public Void floatShape(FloatShape shape) {
            return null;
        }

        @Override
        public Void documentShape(DocumentShape shape) {
            return null;
        }

        @Override
        public Void doubleShape(DoubleShape shape) {
            return null;
        }

        @Override
        public Void bigIntegerShape(BigIntegerShape shape) {
            return null;
        }

        @Override
        public Void bigDecimalShape(BigDecimalShape shape) {
            return null;
        }

        @Override
        public Void stringShape(StringShape shape) {
            return null;
        }

        @Override
        public Void structureShape(StructureShape shape) {
            for (MemberShape member : shape.members()) {
                if (TraitCodegenUtils.isNullableMember(member)) {
                    writer.write("this.$L = $L;", symbolProvider.toMemberName(member), getBuilderValue(member));
                } else {
                    writer.write("this.$1L = $2T.requiredState($1S, $3L);",
                            symbolProvider.toMemberName(member), SmithyBuilder.class, getBuilderValue(member));
                }
            }
            return null;
        }

        @Override
        public Void unionShape(UnionShape shape) {
            throw new UnsupportedOperationException("Does not support shape of type " + shape.getType());
        }

        @Override
        public Void blobShape(BlobShape shape) {
            throw new UnsupportedOperationException("Does not support shape of type " + shape.getType());
        }

        @Override
        public Void memberShape(MemberShape shape) {
            throw new UnsupportedOperationException("Does not support shape of type " + shape.getType());
        }

        @Override
        public Void timestampShape(TimestampShape shape) {
            return null;
        }

        private String getBuilderValue(MemberShape member) {
            String memberName = symbolProvider.toMemberName(member);

            // If the member requires a builderRef we need to copy that builder ref value rather than use it directly.
            if (symbolProvider.toSymbol(member).getProperty(SymbolProperties.BUILDER_REF_INITIALIZER).isPresent()) {
                if (TraitCodegenUtils.isNullableMember(member)) {
                    return writer.format("builder.$1L.hasValue() ? builder.$1L.copy() : null", memberName);
                } else {
                    return writer.format("builder.$1L.copy()", memberName);
                }
            }
            return writer.format("builder.$L", memberName);
        }

        private void writeValuesInitializer() {
            writer.writeWithNoFormatting("this.values = builder.values.copy();");
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy