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