Home | History | Annotate | Download | only in generator
      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