aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/org/zwobble/couscous/ast/ClassNodeBuilder.java
blob: 5981e34688046bf279d9f21417a1919ec237c7d6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
package org.zwobble.couscous.ast;

import java.util.Optional;
import java.util.function.Function;

import com.google.common.collect.ImmutableList;

import static org.zwobble.couscous.ast.FormalArgumentNode.formalArg;
import static org.zwobble.couscous.ast.VariableDeclaration.var;

public class ClassNodeBuilder {
    private final TypeName name;
    private final ImmutableList.Builder<FieldDeclarationNode> fields;
    private Optional<ConstructorNode> constructor;
    private final ImmutableList.Builder<MethodNode> methods;

    public ClassNodeBuilder(TypeName name) {
        this.name = name;
        this.fields = ImmutableList.builder();
        this.constructor = Optional.empty();
        this.methods = ImmutableList.builder();

    }

    public ClassNodeBuilder(String name) {
        this(TypeName.of(name));
    }

    public ClassNodeBuilder field(String name, TypeName type) {
        return field(FieldDeclarationNode.field(name, type));
    }

    public ClassNodeBuilder field(FieldDeclarationNode field) {
        this.fields.add(field);
        return this;
    }

    public ClassNodeBuilder constructor(ConstructorNode constructor) {
        this.constructor = Optional.of(constructor);
        return this;
    }
    
    public ClassNodeBuilder constructor(Function<MethodBuilder<ConstructorNode>, MethodBuilder<ConstructorNode>> build) {
        return constructor(build.apply(constructorBuilder()).build());
    }

    private MethodBuilder<ConstructorNode> constructorBuilder() {
        return new MethodBuilder<>(builder ->
            ConstructorNode.constructor(
                builder.arguments.build(),
                builder.statements.build()));
    }

    public ClassNodeBuilder method(MethodNode method) {
        this.methods.add(method);
        return this;
    }
    
    public ClassNodeBuilder staticMethod(String name, Function<MethodBuilder<MethodNode>, MethodBuilder<MethodNode>> build) {
        return method(name, true, build);
    }
    
    public ClassNodeBuilder method(String name, Function<MethodBuilder<MethodNode>, MethodBuilder<MethodNode>> build) {
        return method(name, false, build);
    }
    
    public ClassNodeBuilder method(String name, boolean isStatic, Function<MethodBuilder<MethodNode>, MethodBuilder<MethodNode>> build) {
        this.methods.add(build.apply(methodBuilder(name, isStatic)).build());
        return this;
    }

    private MethodBuilder<MethodNode> methodBuilder(String name, boolean isStatic) {
        return new MethodBuilder<>(builder -> MethodNode.builder(name)
            .isStatic(isStatic)
            .annotations(builder.annotations.build())
            .arguments(builder.arguments.build())
            .body(builder.statements.build())
            .build());
    }
    
    public ClassNode build() {
        return ClassNode.declareClass(
            name,
            fields.build(),
            constructor.orElse(ConstructorNode.DEFAULT),
            methods.build());
    }

    public class MethodBuilder<T> {
        private final Function<MethodBuilder<?>, T> build;
        private final ImmutableList.Builder<AnnotationNode> annotations;
        private final ImmutableList.Builder<FormalArgumentNode> arguments;
        private final ImmutableList.Builder<StatementNode> statements;

        public MethodBuilder(Function<MethodBuilder<?>, T> build) {
            this.build = build;
            this.annotations = ImmutableList.builder();
            this.arguments = ImmutableList.builder();
            this.statements = ImmutableList.builder();
        }
        
        public MethodBuilder<T> annotation(TypeName type) {
            annotations.add(AnnotationNode.annotation(type));
            return this;
        }
        
        public ThisReferenceNode thisReference() {
            return ThisReferenceNode.thisReference(name);
        }
        
        public MethodBuilder<T> argument(String id, String name, TypeName type) {
            arguments.add(formalArg(var(id, name, type)));
            return this;
        }
        
        public MethodBuilder<T> argument(FormalArgumentNode argument) {
            arguments.add(argument);
            return this;
        }
        
        public MethodBuilder<T> statement(StatementNode statement) {
            statements.add(statement);
            return this;
        }

        public T build() {
            return this.build.apply(this);
        }
    }
}