diff options
Diffstat (limited to 'javaparser-core/src/main/java/com/github/javaparser/ast/validator/chunks/ModifierValidator.java')
-rw-r--r-- | javaparser-core/src/main/java/com/github/javaparser/ast/validator/chunks/ModifierValidator.java | 211 |
1 files changed, 211 insertions, 0 deletions
diff --git a/javaparser-core/src/main/java/com/github/javaparser/ast/validator/chunks/ModifierValidator.java b/javaparser-core/src/main/java/com/github/javaparser/ast/validator/chunks/ModifierValidator.java new file mode 100644 index 000000000..0a01c3349 --- /dev/null +++ b/javaparser-core/src/main/java/com/github/javaparser/ast/validator/chunks/ModifierValidator.java @@ -0,0 +1,211 @@ +package com.github.javaparser.ast.validator.chunks; + +import com.github.javaparser.ast.Modifier; +import com.github.javaparser.ast.body.*; +import com.github.javaparser.ast.expr.LambdaExpr; +import com.github.javaparser.ast.expr.VariableDeclarationExpr; +import com.github.javaparser.ast.modules.ModuleRequiresStmt; +import com.github.javaparser.ast.nodeTypes.NodeWithModifiers; +import com.github.javaparser.ast.nodeTypes.NodeWithTokenRange; +import com.github.javaparser.ast.stmt.CatchClause; +import com.github.javaparser.ast.validator.ProblemReporter; +import com.github.javaparser.ast.validator.VisitorValidator; +import com.github.javaparser.utils.SeparatedItemStringBuilder; + +import java.util.ArrayList; +import java.util.List; + +import static com.github.javaparser.ast.Modifier.*; +import static java.util.Arrays.asList; + + +/** + * Verifies that only allowed modifiers are used where modifiers are expected. + */ +public class ModifierValidator extends VisitorValidator { + private final Modifier[] interfaceWithNothingSpecial = new Modifier[]{PUBLIC, PROTECTED, ABSTRACT, FINAL, SYNCHRONIZED, NATIVE, STRICTFP}; + private final Modifier[] interfaceWithStaticAndDefault = new Modifier[]{PUBLIC, PROTECTED, ABSTRACT, STATIC, FINAL, SYNCHRONIZED, NATIVE, STRICTFP, DEFAULT}; + private final Modifier[] interfaceWithStaticAndDefaultAndPrivate = new Modifier[]{PUBLIC, PROTECTED, PRIVATE, ABSTRACT, STATIC, FINAL, SYNCHRONIZED, NATIVE, STRICTFP, DEFAULT}; + + private final boolean hasStrictfp; + private final boolean hasDefaultAndStaticInterfaceMethods; + private final boolean hasPrivateInterfaceMethods; + + public ModifierValidator(boolean hasStrictfp, boolean hasDefaultAndStaticInterfaceMethods, boolean hasPrivateInterfaceMethods) { + this.hasStrictfp = hasStrictfp; + this.hasDefaultAndStaticInterfaceMethods = hasDefaultAndStaticInterfaceMethods; + this.hasPrivateInterfaceMethods = hasPrivateInterfaceMethods; + } + + @Override + public void visit(ClassOrInterfaceDeclaration n, ProblemReporter reporter) { + if (n.isInterface()) { + validateInterfaceModifiers(n, reporter); + } else { + validateClassModifiers(n, reporter); + } + super.visit(n, reporter); + } + + private void validateClassModifiers(ClassOrInterfaceDeclaration n, ProblemReporter reporter) { + if (n.isTopLevelType()) { + validateModifiers(n, reporter, PUBLIC, ABSTRACT, FINAL, STRICTFP); + } else if (n.isNestedType()) { + validateModifiers(n, reporter, PUBLIC, PROTECTED, PRIVATE, ABSTRACT, STATIC, FINAL, STRICTFP); + } else if (n.isLocalClassDeclaration()) { + validateModifiers(n, reporter, ABSTRACT, FINAL, STRICTFP); + } + } + + private void validateInterfaceModifiers(TypeDeclaration<?> n, ProblemReporter reporter) { + if (n.isTopLevelType()) { + validateModifiers(n, reporter, PUBLIC, ABSTRACT, STRICTFP); + } else if (n.isNestedType()) { + validateModifiers(n, reporter, PUBLIC, PROTECTED, PRIVATE, ABSTRACT, STATIC, STRICTFP); + } + } + + @Override + public void visit(EnumDeclaration n, ProblemReporter reporter) { + if (n.isTopLevelType()) { + validateModifiers(n, reporter, PUBLIC, STRICTFP); + } else if (n.isNestedType()) { + validateModifiers(n, reporter, PUBLIC, PROTECTED, PRIVATE, STATIC, STRICTFP); + } + super.visit(n, reporter); + } + + @Override + public void visit(AnnotationDeclaration n, ProblemReporter reporter) { + validateInterfaceModifiers(n, reporter); + super.visit(n, reporter); + } + + @Override + public void visit(AnnotationMemberDeclaration n, ProblemReporter reporter) { + validateModifiers(n, reporter, PUBLIC, ABSTRACT); + super.visit(n, reporter); + } + + @Override + public void visit(ConstructorDeclaration n, ProblemReporter reporter) { + validateModifiers(n, reporter, PUBLIC, PROTECTED, PRIVATE); + n.getParameters().forEach(p -> validateModifiers(p, reporter, FINAL)); + super.visit(n, reporter); + } + + @Override + public void visit(FieldDeclaration n, ProblemReporter reporter) { + validateModifiers(n, reporter, PUBLIC, PROTECTED, PRIVATE, STATIC, FINAL, TRANSIENT, VOLATILE); + super.visit(n, reporter); + } + + @Override + public void visit(MethodDeclaration n, ProblemReporter reporter) { + if (n.isAbstract()) { + final SeparatedItemStringBuilder builder = new SeparatedItemStringBuilder("Cannot be 'abstract' and also '", "', '", "'."); + for (Modifier m : asList(PRIVATE, STATIC, FINAL, NATIVE, STRICTFP, SYNCHRONIZED)) { + if (n.getModifiers().contains(m)) { + builder.append(m.asString()); + } + } + if (builder.hasItems()) { + reporter.report(n, builder.toString()); + } + } + if (n.getParentNode().isPresent()) { + if (n.getParentNode().get() instanceof ClassOrInterfaceDeclaration) { + if (((ClassOrInterfaceDeclaration) n.getParentNode().get()).isInterface()) { + if (hasDefaultAndStaticInterfaceMethods) { + if (hasPrivateInterfaceMethods) { + validateModifiers(n, reporter, interfaceWithStaticAndDefaultAndPrivate); + } else { + validateModifiers(n, reporter, interfaceWithStaticAndDefault); + } + } else { + validateModifiers(n, reporter, interfaceWithNothingSpecial); + } + } else { + validateModifiers(n, reporter, PUBLIC, PROTECTED, PRIVATE, ABSTRACT, STATIC, FINAL, SYNCHRONIZED, NATIVE, STRICTFP); + } + } + } + n.getParameters().forEach(p -> validateModifiers(p, reporter, FINAL)); + super.visit(n, reporter); + } + + @Override + public void visit(LambdaExpr n, ProblemReporter reporter) { + n.getParameters().forEach(p -> { + // Final is not allowed on inferred parameters, but those get caught by the parser. + validateModifiers(p, reporter, FINAL); + }); + super.visit(n, reporter); + } + + @Override + public void visit(CatchClause n, ProblemReporter reporter) { + validateModifiers(n.getParameter(), reporter, FINAL); + super.visit(n, reporter); + } + + @Override + public void visit(VariableDeclarationExpr n, ProblemReporter reporter) { + validateModifiers(n, reporter, FINAL); + super.visit(n, reporter); + } + + @Override + public void visit(ModuleRequiresStmt n, ProblemReporter reporter) { + validateModifiers(n, reporter, TRANSITIVE, STATIC); + super.visit(n, reporter); + } + + private <T extends NodeWithModifiers<?> & NodeWithTokenRange<?>> void validateModifiers(T n, ProblemReporter reporter, Modifier... allowedModifiers) { + validateAtMostOneOf(n, reporter, PUBLIC, PROTECTED, PRIVATE); + validateAtMostOneOf(n, reporter, FINAL, ABSTRACT); + if (hasStrictfp) { + validateAtMostOneOf(n, reporter, NATIVE, STRICTFP); + } else { + allowedModifiers = removeModifierFromArray(STRICTFP, allowedModifiers); + } + for (Modifier m : n.getModifiers()) { + if (!arrayContains(allowedModifiers, m)) { + reporter.report(n, "'%s' is not allowed here.", m.asString()); + } + } + } + + private Modifier[] removeModifierFromArray(Modifier m, Modifier[] allowedModifiers) { + final List<Modifier> newModifiers = new ArrayList<>(asList(allowedModifiers)); + newModifiers.remove(m); + allowedModifiers = newModifiers.toArray(new Modifier[0]); + return allowedModifiers; + } + + private boolean arrayContains(Object[] items, Object searchItem) { + for (Object o : items) { + if (o == searchItem) { + return true; + } + } + return false; + } + + private <T extends NodeWithModifiers<?> & NodeWithTokenRange<?>> void validateAtMostOneOf(T t, ProblemReporter reporter, Modifier... modifiers) { + List<Modifier> foundModifiers = new ArrayList<>(); + for (Modifier m : modifiers) { + if (t.getModifiers().contains(m)) { + foundModifiers.add(m); + } + } + if (foundModifiers.size() > 1) { + SeparatedItemStringBuilder builder = new SeparatedItemStringBuilder("Can have only one of '", "', '", "'."); + for (Modifier m : foundModifiers) { + builder.append(m.asString()); + } + reporter.report(t, builder.toString()); + } + } + +} |