1 package com.github.javaparser.generator; 2 3 import com.github.javaparser.ast.Node; 4 import com.github.javaparser.ast.body.CallableDeclaration; 5 import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration; 6 import com.github.javaparser.ast.body.MethodDeclaration; 7 import com.github.javaparser.ast.expr.Expression; 8 import com.github.javaparser.ast.expr.StringLiteralExpr; 9 import com.github.javaparser.ast.nodeTypes.NodeWithAnnotations; 10 import com.github.javaparser.utils.SourceRoot; 11 12 import javax.annotation.Generated; 13 import java.util.List; 14 15 import static com.github.javaparser.ast.NodeList.toNodeList; 16 import static com.github.javaparser.utils.CodeGenerationUtils.f; 17 18 /** 19 * A general pattern that the generators in this module will follow. 20 */ 21 public abstract class Generator { 22 protected final SourceRoot sourceRoot; 23 24 protected Generator(SourceRoot sourceRoot) { 25 this.sourceRoot = sourceRoot; 26 } 27 28 public abstract void generate() throws Exception; 29 30 protected <T extends Node & NodeWithAnnotations<?>> void annotateGenerated(T node) { 31 annotate(node, Generated.class, new StringLiteralExpr(getClass().getName())); 32 } 33 34 protected <T extends Node & NodeWithAnnotations<?>> void annotateSuppressWarnings(T node) { 35 annotate(node, SuppressWarnings.class, new StringLiteralExpr("unchecked")); 36 } 37 38 protected void annotateOverridden(MethodDeclaration method) { 39 annotate(method, Override.class, null); 40 } 41 42 private <T extends Node & NodeWithAnnotations<?>> void annotate(T node, Class<?> annotation, Expression content) { 43 node.setAnnotations( 44 node.getAnnotations().stream() 45 .filter(a -> !a.getNameAsString().equals(annotation.getSimpleName())) 46 .collect(toNodeList())); 47 48 if (content != null) { 49 node.addSingleMemberAnnotation(annotation.getSimpleName(), content); 50 } else { 51 node.addMarkerAnnotation(annotation.getSimpleName()); 52 } 53 node.tryAddImportToParentCompilationUnit(annotation); 54 } 55 56 /** 57 * Utility method that looks for a method or constructor with an identical signature as "callable" and replaces it 58 * with callable. If not found, adds callable. When the new callable has no javadoc, any old javadoc will be kept. 59 */ 60 protected void addOrReplaceWhenSameSignature(ClassOrInterfaceDeclaration containingClassOrInterface, CallableDeclaration<?> callable) { 61 addMethod(containingClassOrInterface, callable, () -> containingClassOrInterface.addMember(callable)); 62 } 63 64 /** 65 * Utility method that looks for a method or constructor with an identical signature as "callable" and replaces it 66 * with callable. If not found, fails. When the new callable has no javadoc, any old javadoc will be kept. The 67 * method or constructor is annotated with the generator class. 68 */ 69 protected void replaceWhenSameSignature(ClassOrInterfaceDeclaration containingClassOrInterface, CallableDeclaration<?> callable) { 70 addMethod(containingClassOrInterface, callable, 71 () -> { 72 throw new AssertionError(f("Wanted to regenerate a method with signature %s in %s, but it wasn't there.", callable.getSignature(), containingClassOrInterface.getNameAsString())); 73 }); 74 } 75 76 private void addMethod( 77 ClassOrInterfaceDeclaration containingClassOrInterface, 78 CallableDeclaration<?> callable, 79 Runnable onNoExistingMethod) { 80 List<CallableDeclaration<?>> existingCallables = containingClassOrInterface.getCallablesWithSignature(callable.getSignature()); 81 if (existingCallables.isEmpty()) { 82 onNoExistingMethod.run(); 83 return; 84 } 85 if (existingCallables.size() > 1) { 86 throw new AssertionError(f("Wanted to regenerate a method with signature %s in %s, but found more than one.", callable.getSignature(), containingClassOrInterface.getNameAsString())); 87 } 88 final CallableDeclaration<?> existingCallable = existingCallables.get(0); 89 callable.setJavadocComment(callable.getJavadocComment().orElse(existingCallable.getJavadocComment().orElse(null))); 90 annotateGenerated(callable); 91 containingClassOrInterface.getMembers().replace(existingCallable, callable); 92 } 93 94 /** 95 * Removes all methods from containingClassOrInterface that have the same signature as callable. This is not used by 96 * any code, but it is useful when changing a generator and you need to get rid of a set of outdated methods. 97 */ 98 protected void removeMethodWithSameSignature(ClassOrInterfaceDeclaration containingClassOrInterface, CallableDeclaration<?> callable) { 99 for (CallableDeclaration<?> existingCallable : containingClassOrInterface.getCallablesWithSignature(callable.getSignature())) { 100 containingClassOrInterface.remove(existingCallable); 101 } 102 } 103 104 } 105