1   ////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code for adherence to a set of rules.
3   // Copyright (C) 2001-2019 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.LinkedList;
24  import java.util.List;
25  import java.util.Set;
26  import java.util.stream.Collectors;
27  
28  import com.puppycrawl.tools.checkstyle.StatelessCheck;
29  import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
30  import com.puppycrawl.tools.checkstyle.api.DetailAST;
31  import com.puppycrawl.tools.checkstyle.api.FullIdent;
32  import com.puppycrawl.tools.checkstyle.api.TokenTypes;
33  import com.puppycrawl.tools.checkstyle.utils.CheckUtil;
34  
35  /**
36   * Catching java.lang.Exception, java.lang.Error or java.lang.RuntimeException
37   * is almost never acceptable.
38   */
39  @StatelessCheck
40  public final class IllegalCatchCheck extends AbstractCheck {
41  
42      /**
43       * A key is pointing to the warning message text in "messages.properties"
44       * file.
45       */
46      public static final String MSG_KEY = "illegal.catch";
47  
48      /** Illegal class names. */
49      private final Set<String> illegalClassNames = Arrays.stream(new String[] {"Exception", "Error",
50          "RuntimeException", "Throwable", "java.lang.Error", "java.lang.Exception",
51          "java.lang.RuntimeException", "java.lang.Throwable", }).collect(Collectors.toSet());
52  
53      /**
54       * Set the list of illegal classes.
55       *
56       * @param classNames
57       *            array of illegal exception classes
58       */
59      public void setIllegalClassNames(final String... classNames) {
60          illegalClassNames.clear();
61          illegalClassNames.addAll(
62                  CheckUtil.parseClassNames(classNames));
63      }
64  
65      @Override
66      public int[] getDefaultTokens() {
67          return getRequiredTokens();
68      }
69  
70      @Override
71      public int[] getRequiredTokens() {
72          return new int[] {TokenTypes.LITERAL_CATCH};
73      }
74  
75      @Override
76      public int[] getAcceptableTokens() {
77          return getRequiredTokens();
78      }
79  
80      @Override
81      public void visitToken(DetailAST detailAST) {
82          final DetailAST parameterDef =
83              detailAST.findFirstToken(TokenTypes.PARAMETER_DEF);
84          final DetailAST excTypeParent =
85                  parameterDef.findFirstToken(TokenTypes.TYPE);
86          final List<DetailAST> excTypes = getAllExceptionTypes(excTypeParent);
87  
88          for (DetailAST excType : excTypes) {
89              final FullIdent ident = FullIdent.createFullIdent(excType);
90  
91              if (illegalClassNames.contains(ident.getText())) {
92                  log(detailAST, MSG_KEY, ident.getText());
93              }
94          }
95      }
96  
97      /**
98       * Finds all exception types in current catch.
99       * We need it till we can have few different exception types into one catch.
100      * @param parentToken - parent node for types (TYPE or BOR)
101      * @return list, that contains all exception types in current catch
102      */
103     private static List<DetailAST> getAllExceptionTypes(DetailAST parentToken) {
104         DetailAST currentNode = parentToken.getFirstChild();
105         final List<DetailAST> exceptionTypes = new LinkedList<>();
106         if (currentNode.getType() == TokenTypes.BOR) {
107             exceptionTypes.addAll(getAllExceptionTypes(currentNode));
108             currentNode = currentNode.getNextSibling();
109             if (currentNode != null) {
110                 exceptionTypes.add(currentNode);
111             }
112         }
113         else {
114             exceptionTypes.add(currentNode);
115             currentNode = currentNode.getNextSibling();
116             while (currentNode != null) {
117                 exceptionTypes.add(currentNode);
118                 currentNode = currentNode.getNextSibling();
119             }
120         }
121         return exceptionTypes;
122     }
123 
124 }