View Javadoc
1   ////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code for adherence to a set of rules.
3   // Copyright (C) 2001-2018 the original author or authors.
4   //
5   // This library is free software; you can redistribute it and/or
6   // modify it under the terms of the GNU Lesser General Public
7   // License as published by the Free Software Foundation; either
8   // version 2.1 of the License, or (at your option) any later version.
9   //
10  // This library is distributed in the hope that it will be useful,
11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  // Lesser General Public License for more details.
14  //
15  // You should have received a copy of the GNU Lesser General Public
16  // License along with this library; if not, write to the Free Software
17  // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  ////////////////////////////////////////////////////////////////////////////////
19  
20  package com.puppycrawl.tools.checkstyle.checks.coding;
21  
22  import java.util.Arrays;
23  import java.util.Collections;
24  import java.util.Set;
25  import java.util.stream.Collectors;
26  
27  import com.puppycrawl.tools.checkstyle.StatelessCheck;
28  import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
29  import com.puppycrawl.tools.checkstyle.api.DetailAST;
30  import com.puppycrawl.tools.checkstyle.api.FullIdent;
31  import com.puppycrawl.tools.checkstyle.api.TokenTypes;
32  import com.puppycrawl.tools.checkstyle.utils.AnnotationUtility;
33  import com.puppycrawl.tools.checkstyle.utils.CheckUtils;
34  
35  /**
36   * <p>
37   * Throwing java.lang.Error or java.lang.RuntimeException
38   * is almost never acceptable.
39   * </p>
40   * Check has following properties:
41   * <p>
42   * <b>illegalClassNames</b> - throw class names to reject.
43   * </p>
44   * <p>
45   * <b>ignoredMethodNames</b> - names of methods to ignore.
46   * </p>
47   * <p>
48   * <b>ignoreOverriddenMethods</b> - ignore checking overridden methods (marked with Override
49   *  or java.lang.Override annotation) default value is <b>true</b>.
50   * </p>
51   *
52   */
53  @StatelessCheck
54  public final class IllegalThrowsCheck extends AbstractCheck {
55  
56      /**
57       * A key is pointing to the warning message text in "messages.properties"
58       * file.
59       */
60      public static final String MSG_KEY = "illegal.throw";
61  
62      /** Methods which should be ignored. */
63      private final Set<String> ignoredMethodNames =
64          Arrays.stream(new String[] {"finalize", }).collect(Collectors.toSet());
65  
66      /** Illegal class names. */
67      private final Set<String> illegalClassNames = Arrays.stream(
68          new String[] {"Error", "RuntimeException", "Throwable", "java.lang.Error",
69                        "java.lang.RuntimeException", "java.lang.Throwable", })
70          .collect(Collectors.toSet());
71  
72      /** Property for ignoring overridden methods. */
73      private boolean ignoreOverriddenMethods = true;
74  
75      /**
76       * Set the list of illegal classes.
77       *
78       * @param classNames
79       *            array of illegal exception classes
80       */
81      public void setIllegalClassNames(final String... classNames) {
82          illegalClassNames.clear();
83          illegalClassNames.addAll(
84                  CheckUtils.parseClassNames(classNames));
85      }
86  
87      @Override
88      public int[] getDefaultTokens() {
89          return getRequiredTokens();
90      }
91  
92      @Override
93      public int[] getRequiredTokens() {
94          return new int[] {TokenTypes.LITERAL_THROWS};
95      }
96  
97      @Override
98      public int[] getAcceptableTokens() {
99          return getRequiredTokens();
100     }
101 
102     @Override
103     public void visitToken(DetailAST detailAST) {
104         final DetailAST methodDef = detailAST.getParent();
105         // Check if the method with the given name should be ignored.
106         if (!isIgnorableMethod(methodDef)) {
107             DetailAST token = detailAST.getFirstChild();
108             while (token != null) {
109                 if (token.getType() != TokenTypes.COMMA) {
110                     final FullIdent ident = FullIdent.createFullIdent(token);
111                     if (illegalClassNames.contains(ident.getText())) {
112                         log(token, MSG_KEY, ident.getText());
113                     }
114                 }
115                 token = token.getNextSibling();
116             }
117         }
118     }
119 
120     /**
121      * Checks if current method is ignorable due to Check's properties.
122      * @param methodDef {@link TokenTypes#METHOD_DEF METHOD_DEF}
123      * @return true if method is ignorable.
124      */
125     private boolean isIgnorableMethod(DetailAST methodDef) {
126         return shouldIgnoreMethod(methodDef.findFirstToken(TokenTypes.IDENT).getText())
127             || ignoreOverriddenMethods
128                && (AnnotationUtility.containsAnnotation(methodDef, "Override")
129                   || AnnotationUtility.containsAnnotation(methodDef, "java.lang.Override"));
130     }
131 
132     /**
133      * Check if the method is specified in the ignore method list.
134      * @param name the name to check
135      * @return whether the method with the passed name should be ignored
136      */
137     private boolean shouldIgnoreMethod(String name) {
138         return ignoredMethodNames.contains(name);
139     }
140 
141     /**
142      * Set the list of ignore method names.
143      * @param methodNames array of ignored method names
144      */
145     public void setIgnoredMethodNames(String... methodNames) {
146         ignoredMethodNames.clear();
147         Collections.addAll(ignoredMethodNames, methodNames);
148     }
149 
150     /**
151      * Sets <b>ignoreOverriddenMethods</b> property value.
152      * @param ignoreOverriddenMethods Check's property.
153      */
154     public void setIgnoreOverriddenMethods(boolean ignoreOverriddenMethods) {
155         this.ignoreOverriddenMethods = ignoreOverriddenMethods;
156     }
157 
158 }