Home | History | Annotate | Download | only in chunks
      1 package com.github.javaparser.ast.validator.chunks;
      2 
      3 import com.github.javaparser.ast.Modifier;
      4 import com.github.javaparser.ast.body.*;
      5 import com.github.javaparser.ast.expr.LambdaExpr;
      6 import com.github.javaparser.ast.expr.VariableDeclarationExpr;
      7 import com.github.javaparser.ast.modules.ModuleRequiresStmt;
      8 import com.github.javaparser.ast.nodeTypes.NodeWithModifiers;
      9 import com.github.javaparser.ast.nodeTypes.NodeWithTokenRange;
     10 import com.github.javaparser.ast.stmt.CatchClause;
     11 import com.github.javaparser.ast.validator.ProblemReporter;
     12 import com.github.javaparser.ast.validator.VisitorValidator;
     13 import com.github.javaparser.utils.SeparatedItemStringBuilder;
     14 
     15 import java.util.ArrayList;
     16 import java.util.List;
     17 
     18 import static com.github.javaparser.ast.Modifier.*;
     19 import static java.util.Arrays.asList;
     20 
     21 
     22 /**
     23  * Verifies that only allowed modifiers are used where modifiers are expected.
     24  */
     25 public class ModifierValidator extends VisitorValidator {
     26     private final Modifier[] interfaceWithNothingSpecial = new Modifier[]{PUBLIC, PROTECTED, ABSTRACT, FINAL, SYNCHRONIZED, NATIVE, STRICTFP};
     27     private final Modifier[] interfaceWithStaticAndDefault = new Modifier[]{PUBLIC, PROTECTED, ABSTRACT, STATIC, FINAL, SYNCHRONIZED, NATIVE, STRICTFP, DEFAULT};
     28     private final Modifier[] interfaceWithStaticAndDefaultAndPrivate = new Modifier[]{PUBLIC, PROTECTED, PRIVATE, ABSTRACT, STATIC, FINAL, SYNCHRONIZED, NATIVE, STRICTFP, DEFAULT};
     29 
     30     private final boolean hasStrictfp;
     31     private final boolean hasDefaultAndStaticInterfaceMethods;
     32     private final boolean hasPrivateInterfaceMethods;
     33 
     34     public ModifierValidator(boolean hasStrictfp, boolean hasDefaultAndStaticInterfaceMethods, boolean hasPrivateInterfaceMethods) {
     35         this.hasStrictfp = hasStrictfp;
     36         this.hasDefaultAndStaticInterfaceMethods = hasDefaultAndStaticInterfaceMethods;
     37         this.hasPrivateInterfaceMethods = hasPrivateInterfaceMethods;
     38     }
     39 
     40     @Override
     41     public void visit(ClassOrInterfaceDeclaration n, ProblemReporter reporter) {
     42         if (n.isInterface()) {
     43             validateInterfaceModifiers(n, reporter);
     44         } else {
     45             validateClassModifiers(n, reporter);
     46         }
     47         super.visit(n, reporter);
     48     }
     49 
     50     private void validateClassModifiers(ClassOrInterfaceDeclaration n, ProblemReporter reporter) {
     51         if (n.isTopLevelType()) {
     52             validateModifiers(n, reporter, PUBLIC, ABSTRACT, FINAL, STRICTFP);
     53         } else if (n.isNestedType()) {
     54             validateModifiers(n, reporter, PUBLIC, PROTECTED, PRIVATE, ABSTRACT, STATIC, FINAL, STRICTFP);
     55         } else if (n.isLocalClassDeclaration()) {
     56             validateModifiers(n, reporter, ABSTRACT, FINAL, STRICTFP);
     57         }
     58     }
     59 
     60     private void validateInterfaceModifiers(TypeDeclaration<?> n, ProblemReporter reporter) {
     61         if (n.isTopLevelType()) {
     62             validateModifiers(n, reporter, PUBLIC, ABSTRACT, STRICTFP);
     63         } else if (n.isNestedType()) {
     64             validateModifiers(n, reporter, PUBLIC, PROTECTED, PRIVATE, ABSTRACT, STATIC, STRICTFP);
     65         }
     66     }
     67 
     68     @Override
     69     public void visit(EnumDeclaration n, ProblemReporter reporter) {
     70         if (n.isTopLevelType()) {
     71             validateModifiers(n, reporter, PUBLIC, STRICTFP);
     72         } else if (n.isNestedType()) {
     73             validateModifiers(n, reporter, PUBLIC, PROTECTED, PRIVATE, STATIC, STRICTFP);
     74         }
     75         super.visit(n, reporter);
     76     }
     77 
     78     @Override
     79     public void visit(AnnotationDeclaration n, ProblemReporter reporter) {
     80         validateInterfaceModifiers(n, reporter);
     81         super.visit(n, reporter);
     82     }
     83 
     84     @Override
     85     public void visit(AnnotationMemberDeclaration n, ProblemReporter reporter) {
     86         validateModifiers(n, reporter, PUBLIC, ABSTRACT);
     87         super.visit(n, reporter);
     88     }
     89 
     90     @Override
     91     public void visit(ConstructorDeclaration n, ProblemReporter reporter) {
     92         validateModifiers(n, reporter, PUBLIC, PROTECTED, PRIVATE);
     93         n.getParameters().forEach(p -> validateModifiers(p, reporter, FINAL));
     94         super.visit(n, reporter);
     95     }
     96 
     97     @Override
     98     public void visit(FieldDeclaration n, ProblemReporter reporter) {
     99         validateModifiers(n, reporter, PUBLIC, PROTECTED, PRIVATE, STATIC, FINAL, TRANSIENT, VOLATILE);
    100         super.visit(n, reporter);
    101     }
    102 
    103     @Override
    104     public void visit(MethodDeclaration n, ProblemReporter reporter) {
    105         if (n.isAbstract()) {
    106             final SeparatedItemStringBuilder builder = new SeparatedItemStringBuilder("Cannot be 'abstract' and also '", "', '", "'.");
    107             for (Modifier m : asList(PRIVATE, STATIC, FINAL, NATIVE, STRICTFP, SYNCHRONIZED)) {
    108                 if (n.getModifiers().contains(m)) {
    109                     builder.append(m.asString());
    110                 }
    111             }
    112             if (builder.hasItems()) {
    113                 reporter.report(n, builder.toString());
    114             }
    115         }
    116         if (n.getParentNode().isPresent()) {
    117             if (n.getParentNode().get() instanceof ClassOrInterfaceDeclaration) {
    118                 if (((ClassOrInterfaceDeclaration) n.getParentNode().get()).isInterface()) {
    119                     if (hasDefaultAndStaticInterfaceMethods) {
    120                         if (hasPrivateInterfaceMethods) {
    121                             validateModifiers(n, reporter, interfaceWithStaticAndDefaultAndPrivate);
    122                         } else {
    123                             validateModifiers(n, reporter, interfaceWithStaticAndDefault);
    124                         }
    125                     } else {
    126                         validateModifiers(n, reporter, interfaceWithNothingSpecial);
    127                     }
    128                 } else {
    129                     validateModifiers(n, reporter, PUBLIC, PROTECTED, PRIVATE, ABSTRACT, STATIC, FINAL, SYNCHRONIZED, NATIVE, STRICTFP);
    130                 }
    131             }
    132         }
    133         n.getParameters().forEach(p -> validateModifiers(p, reporter, FINAL));
    134         super.visit(n, reporter);
    135     }
    136 
    137     @Override
    138     public void visit(LambdaExpr n, ProblemReporter reporter) {
    139         n.getParameters().forEach(p -> {
    140             // Final is not allowed on inferred parameters, but those get caught by the parser.
    141             validateModifiers(p, reporter, FINAL);
    142         });
    143         super.visit(n, reporter);
    144     }
    145 
    146     @Override
    147     public void visit(CatchClause n, ProblemReporter reporter) {
    148         validateModifiers(n.getParameter(), reporter, FINAL);
    149         super.visit(n, reporter);
    150     }
    151 
    152     @Override
    153     public void visit(VariableDeclarationExpr n, ProblemReporter reporter) {
    154         validateModifiers(n, reporter, FINAL);
    155         super.visit(n, reporter);
    156     }
    157 
    158     @Override
    159     public void visit(ModuleRequiresStmt n, ProblemReporter reporter) {
    160         validateModifiers(n, reporter, TRANSITIVE, STATIC);
    161         super.visit(n, reporter);
    162     }
    163 
    164     private <T extends NodeWithModifiers<?> & NodeWithTokenRange<?>> void validateModifiers(T n, ProblemReporter reporter, Modifier... allowedModifiers) {
    165         validateAtMostOneOf(n, reporter, PUBLIC, PROTECTED, PRIVATE);
    166         validateAtMostOneOf(n, reporter, FINAL, ABSTRACT);
    167         if (hasStrictfp) {
    168             validateAtMostOneOf(n, reporter, NATIVE, STRICTFP);
    169         } else {
    170             allowedModifiers = removeModifierFromArray(STRICTFP, allowedModifiers);
    171         }
    172         for (Modifier m : n.getModifiers()) {
    173             if (!arrayContains(allowedModifiers, m)) {
    174                 reporter.report(n, "'%s' is not allowed here.", m.asString());
    175             }
    176         }
    177     }
    178 
    179     private Modifier[] removeModifierFromArray(Modifier m, Modifier[] allowedModifiers) {
    180         final List<Modifier> newModifiers = new ArrayList<>(asList(allowedModifiers));
    181         newModifiers.remove(m);
    182         allowedModifiers = newModifiers.toArray(new Modifier[0]);
    183         return allowedModifiers;
    184     }
    185 
    186     private boolean arrayContains(Object[] items, Object searchItem) {
    187         for (Object o : items) {
    188             if (o == searchItem) {
    189                 return true;
    190             }
    191         }
    192         return false;
    193     }
    194 
    195     private <T extends NodeWithModifiers<?> & NodeWithTokenRange<?>> void validateAtMostOneOf(T t, ProblemReporter reporter, Modifier... modifiers) {
    196         List<Modifier> foundModifiers = new ArrayList<>();
    197         for (Modifier m : modifiers) {
    198             if (t.getModifiers().contains(m)) {
    199                 foundModifiers.add(m);
    200             }
    201         }
    202         if (foundModifiers.size() > 1) {
    203             SeparatedItemStringBuilder builder = new SeparatedItemStringBuilder("Can have only one of '", "', '", "'.");
    204             for (Modifier m : foundModifiers) {
    205                 builder.append(m.asString());
    206             }
    207             reporter.report(t, builder.toString());
    208         }
    209     }
    210 
    211 }
    212