1package com.github.javaparser.generator.core.node;
2
3import com.github.javaparser.ast.CompilationUnit;
4import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
5import com.github.javaparser.ast.body.MethodDeclaration;
6import com.github.javaparser.generator.NodeGenerator;
7import com.github.javaparser.metamodel.BaseNodeMetaModel;
8import com.github.javaparser.metamodel.JavaParserMetaModel;
9import com.github.javaparser.utils.Pair;
10import com.github.javaparser.utils.SourceRoot;
11
12import java.util.Optional;
13import java.util.Set;
14import java.util.function.Consumer;
15
16import static com.github.javaparser.JavaParser.parseBodyDeclaration;
17import static com.github.javaparser.utils.CodeGenerationUtils.f;
18import static com.github.javaparser.utils.Utils.set;
19
20public class TypeCastingGenerator extends NodeGenerator {
21    private final Set<BaseNodeMetaModel> baseNodes = set(
22            JavaParserMetaModel.statementMetaModel,
23            JavaParserMetaModel.expressionMetaModel,
24            JavaParserMetaModel.typeMetaModel,
25            JavaParserMetaModel.moduleStmtMetaModel,
26            JavaParserMetaModel.bodyDeclarationMetaModel,
27            JavaParserMetaModel.commentMetaModel
28    );
29
30    public TypeCastingGenerator(SourceRoot sourceRoot) {
31        super(sourceRoot);
32    }
33
34    @Override
35    protected void generateNode(BaseNodeMetaModel nodeMetaModel, CompilationUnit nodeCu, ClassOrInterfaceDeclaration nodeCoid) throws Exception {
36        Pair<CompilationUnit, ClassOrInterfaceDeclaration> baseCode = null;
37        for (BaseNodeMetaModel baseNode : baseNodes) {
38            if(nodeMetaModel == baseNode) {
39                // We adjust the base models from the child nodes,
40                // so we don't do anything when we *are* the base model.
41                return;
42            }
43            if (nodeMetaModel.isInstanceOfMetaModel(baseNode)) {
44                baseCode = parseNode(baseNode);
45            }
46        }
47
48        if (baseCode == null) {
49            // Node is not a child of one of the base nodes, so we don't want to generate this method for it.
50            return;
51        }
52
53        final String typeName = nodeMetaModel.getTypeName();
54        final ClassOrInterfaceDeclaration baseCoid = baseCode.b;
55        final CompilationUnit baseCu = baseCode.a;
56
57        generateIsType(baseCu, nodeCoid, baseCoid, typeName);
58        generateAsType(baseCu, nodeCoid, baseCoid, typeName);
59        generateToType(nodeCu, baseCu, nodeCoid, baseCoid, typeName);
60        generateIfType(nodeCu, baseCu, nodeCoid, baseCoid, typeName);
61    }
62
63    private void generateAsType(CompilationUnit baseCu, ClassOrInterfaceDeclaration nodeCoid, ClassOrInterfaceDeclaration baseCoid, String typeName) {
64        final MethodDeclaration asTypeBaseMethod = (MethodDeclaration) parseBodyDeclaration(f("public %s as%s() { throw new IllegalStateException(f(\"%%s is not an %s\", this)); }", typeName, typeName, typeName));
65        final MethodDeclaration asTypeNodeMethod = (MethodDeclaration) parseBodyDeclaration(f("@Override public %s as%s() { return this; }", typeName, typeName));
66        addOrReplaceWhenSameSignature(baseCoid, asTypeBaseMethod);
67        addOrReplaceWhenSameSignature(nodeCoid, asTypeNodeMethod);
68        baseCu.addImport("com.github.javaparser.utils.CodeGenerationUtils.f", true, false);
69    }
70
71    private void generateToType(CompilationUnit nodeCu, CompilationUnit baseCu, ClassOrInterfaceDeclaration nodeCoid, ClassOrInterfaceDeclaration baseCoid, String typeName) {
72        baseCu.addImport(Optional.class);
73        nodeCu.addImport(Optional.class);
74        final MethodDeclaration asTypeBaseMethod = (MethodDeclaration) parseBodyDeclaration(f("public Optional<%s> to%s() { return Optional.empty(); }", typeName, typeName, typeName));
75        final MethodDeclaration asTypeNodeMethod = (MethodDeclaration) parseBodyDeclaration(f("@Override public Optional<%s> to%s() { return Optional.of(this); }", typeName, typeName));
76        addOrReplaceWhenSameSignature(baseCoid, asTypeBaseMethod);
77        addOrReplaceWhenSameSignature(nodeCoid, asTypeNodeMethod);
78    }
79
80    private void generateIfType(CompilationUnit nodeCu, CompilationUnit baseCu, ClassOrInterfaceDeclaration nodeCoid, ClassOrInterfaceDeclaration baseCoid, String typeName) {
81        final MethodDeclaration ifTypeBaseMethod = (MethodDeclaration) parseBodyDeclaration(f("public void if%s(Consumer<%s> action) { }", typeName, typeName));
82        final MethodDeclaration ifTypeNodeMethod = (MethodDeclaration) parseBodyDeclaration(f("public void if%s(Consumer<%s> action) { action.accept(this); }", typeName, typeName));
83        addOrReplaceWhenSameSignature(baseCoid, ifTypeBaseMethod);
84        addOrReplaceWhenSameSignature(nodeCoid, ifTypeNodeMethod);
85
86        baseCu.addImport(Consumer.class);
87        nodeCu.addImport(Consumer.class);
88    }
89
90    private void generateIsType(CompilationUnit baseCu, ClassOrInterfaceDeclaration nodeCoid, ClassOrInterfaceDeclaration baseCoid, String typeName) {
91        final MethodDeclaration baseIsTypeMethod = (MethodDeclaration) parseBodyDeclaration(f("public boolean is%s() { return false; }", typeName));
92        final MethodDeclaration overriddenIsTypeMethod = (MethodDeclaration) parseBodyDeclaration(f("@Override public boolean is%s() { return true; }", typeName));
93
94        addOrReplaceWhenSameSignature(nodeCoid, overriddenIsTypeMethod);
95        addOrReplaceWhenSameSignature(baseCoid, baseIsTypeMethod);
96    }
97}
98