001//////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code for adherence to a set of rules. 003// Copyright (C) 2001-2017 the original author or authors. 004// 005// This library is free software; you can redistribute it and/or 006// modify it under the terms of the GNU Lesser General Public 007// License as published by the Free Software Foundation; either 008// version 2.1 of the License, or (at your option) any later version. 009// 010// This library is distributed in the hope that it will be useful, 011// but WITHOUT ANY WARRANTY; without even the implied warranty of 012// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 013// Lesser General Public License for more details. 014// 015// You should have received a copy of the GNU Lesser General Public 016// License along with this library; if not, write to the Free Software 017// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 018//////////////////////////////////////////////////////////////////////////////// 019 020package com.puppycrawl.tools.checkstyle.checks.coding; 021 022import java.util.Arrays; 023import java.util.LinkedList; 024import java.util.List; 025import java.util.Set; 026import java.util.stream.Collectors; 027 028import com.puppycrawl.tools.checkstyle.api.AbstractCheck; 029import com.puppycrawl.tools.checkstyle.api.DetailAST; 030import com.puppycrawl.tools.checkstyle.api.FullIdent; 031import com.puppycrawl.tools.checkstyle.api.TokenTypes; 032 033/** 034 * Catching java.lang.Exception, java.lang.Error or java.lang.RuntimeException 035 * is almost never acceptable. 036 * @author <a href="mailto:simon@redhillconsulting.com.au">Simon Harris</a> 037 * @author <a href="mailto:IliaDubinin91@gmail.com">Ilja Dubinin</a> 038 */ 039public final class IllegalCatchCheck extends AbstractCheck { 040 041 /** 042 * A key is pointing to the warning message text in "messages.properties" 043 * file. 044 */ 045 public static final String MSG_KEY = "illegal.catch"; 046 047 /** Illegal class names. */ 048 private final Set<String> illegalClassNames = Arrays.stream(new String[] {"Exception", "Error", 049 "RuntimeException", "Throwable", "java.lang.Error", "java.lang.Exception", 050 "java.lang.RuntimeException", "java.lang.Throwable", }).collect(Collectors.toSet()); 051 052 /** 053 * Set the list of illegal classes. 054 * 055 * @param classNames 056 * array of illegal exception classes 057 */ 058 public void setIllegalClassNames(final String... classNames) { 059 illegalClassNames.clear(); 060 for (final String name : classNames) { 061 illegalClassNames.add(name); 062 final int lastDot = name.lastIndexOf('.'); 063 if (lastDot > 0 && lastDot < name.length() - 1) { 064 final String shortName = name 065 .substring(name.lastIndexOf('.') + 1); 066 illegalClassNames.add(shortName); 067 } 068 } 069 } 070 071 @Override 072 public int[] getDefaultTokens() { 073 return new int[] {TokenTypes.LITERAL_CATCH}; 074 } 075 076 @Override 077 public int[] getRequiredTokens() { 078 return getDefaultTokens(); 079 } 080 081 @Override 082 public int[] getAcceptableTokens() { 083 return new int[] {TokenTypes.LITERAL_CATCH}; 084 } 085 086 @Override 087 public void visitToken(DetailAST detailAST) { 088 final DetailAST parameterDef = 089 detailAST.findFirstToken(TokenTypes.PARAMETER_DEF); 090 final DetailAST excTypeParent = 091 parameterDef.findFirstToken(TokenTypes.TYPE); 092 final List<DetailAST> excTypes = getAllExceptionTypes(excTypeParent); 093 094 for (DetailAST excType : excTypes) { 095 final FullIdent ident = FullIdent.createFullIdent(excType); 096 097 if (illegalClassNames.contains(ident.getText())) { 098 log(detailAST, MSG_KEY, ident.getText()); 099 } 100 } 101 } 102 103 /** 104 * Finds all exception types in current catch. 105 * We need it till we can have few different exception types into one catch. 106 * @param parentToken - parent node for types (TYPE or BOR) 107 * @return list, that contains all exception types in current catch 108 */ 109 private static List<DetailAST> getAllExceptionTypes(DetailAST parentToken) { 110 DetailAST currentNode = parentToken.getFirstChild(); 111 final List<DetailAST> exceptionTypes = new LinkedList<>(); 112 if (currentNode.getType() == TokenTypes.BOR) { 113 exceptionTypes.addAll(getAllExceptionTypes(currentNode)); 114 currentNode = currentNode.getNextSibling(); 115 if (currentNode != null) { 116 exceptionTypes.add(currentNode); 117 } 118 } 119 else { 120 exceptionTypes.add(currentNode); 121 currentNode = currentNode.getNextSibling(); 122 while (currentNode != null) { 123 exceptionTypes.add(currentNode); 124 currentNode = currentNode.getNextSibling(); 125 } 126 } 127 return exceptionTypes; 128 } 129}