//////////////////////////////////////////////////////////////////////////////// // checkstyle: Checks Java source code for adherence to a set of rules. // Copyright (C) 2001-2017 the original author or authors. // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 2.1 of the License, or (at your option) any later version. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA //////////////////////////////////////////////////////////////////////////////// package com.github.sevntu.checkstyle.checks.coding; import com.puppycrawl.tools.checkstyle.api.AbstractCheck; import com.puppycrawl.tools.checkstyle.api.DetailAST; import com.puppycrawl.tools.checkstyle.api.TokenTypes; /** * This Check highlights variable definition statements where <a href= * "http://docs.oracle.com/javase/7/docs/technotes/guides/language/type-inference-generic-instance-creation.html"> * diamond operator</a> could be used.<br> * <b>Rationale</b>: using diamond operator (introduced in Java 1.7) leads to shorter code<br> * and better code readability. It is suggested by Oracle that the diamond primarily using<br> * for variable declarations.<br><br> * E.g. of statements: * * <p><b>Without diamond operator:</b><br><code> * Map<String, Map<String, Integer>> someMap = * new HashMap<String, Map<String, Integer>>();</code><br> * <b>With diamond operator:</b><br> * <code> * Map<String, Map<String, Integer>> someMap = new HashMap<>(); * </code> * </p> * @author <a href="mailto:nesterenko-aleksey@list.ru">Aleksey Nesterenko</a> */ public class DiamondOperatorForVariableDefinitionCheck extends AbstractCheck { /** A key is pointing to the warning message text in "messages.properties" file. */ public static final String MSG_KEY = "diamond.operator.for.variable.definition"; @Override public int[] getDefaultTokens() { return new int[] {TokenTypes.VARIABLE_DEF}; } @Override public void visitToken(DetailAST variableDefNode) { final DetailAST assignNode = variableDefNode.findFirstToken(TokenTypes.ASSIGN); if (assignNode != null) { final DetailAST newNode = assignNode.getFirstChild().getFirstChild(); // we check only creation by NEW if (newNode.getType() == TokenTypes.LITERAL_NEW) { final DetailAST variableDefNodeType = variableDefNode.findFirstToken(TokenTypes.TYPE); final DetailAST varDefArguments = getFirstTypeArgumentsToken(variableDefNodeType); // generics has to be on left side if (varDefArguments != null && newNode.getLastChild().getType() != TokenTypes.OBJBLOCK // arrays can not be generics && newNode.findFirstToken(TokenTypes.ARRAY_DECLARATOR) == null) { final DetailAST typeArgs = getFirstTypeArgumentsToken(newNode); if (varDefArguments.equalsTree(typeArgs)) { log(typeArgs, MSG_KEY); } } } } } /** * Get first occurrence of TYPE_ARGUMENTS if exists. * @param rootToken the token to start search from. * @return TYPE_ARGUMENTS token if found. */ private static DetailAST getFirstTypeArgumentsToken(DetailAST rootToken) { DetailAST resultNode = rootToken.getFirstChild(); if (resultNode != null) { if (resultNode.getType() == TokenTypes.DOT) { resultNode = resultNode.getFirstChild().getNextSibling(); } final DetailAST childNode = getFirstTypeArgumentsToken(resultNode); if (childNode == null) { resultNode = resultNode.getNextSibling(); } } return resultNode; } }