Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: com/puppycrawl/tools/checkstyle/checks/EqualsHashCodeCheck.java


1   ////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code for adherence to a set of rules.
3   // Copyright (C) 2001-2002  Oliver Burn
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;
21  
22  import java.util.HashMap;
23  import java.util.HashSet;
24  import java.util.Iterator;
25  import java.util.Map;
26  import java.util.Set;
27  
28  import antlr.collections.AST;
29  import com.puppycrawl.tools.checkstyle.api.Check;
30  import com.puppycrawl.tools.checkstyle.api.DetailAST;
31  import com.puppycrawl.tools.checkstyle.api.TokenTypes;
32  
33  /**
34   * <p>
35   * Checks that classes that override equals() also override hashCode().
36   * </p>
37   * <p>
38   * Rationale: The contract of equals() and hashCode() requires that
39   * equal objects have the same hashCode. Hence, whenever you override
40   * equals() you must override hashCode() to ensure that your class can
41   * be used in collections that are hash based.
42   * </p>
43   * <p>
44   * An example of how to configure the check is:
45   * </p>
46   * <pre>
47   * &lt;module name="EqualsHashCode"/&gt;
48   * </pre>
49   * @author lkuehne
50   */
51  public class EqualsHashCodeCheck
52          extends Check
53  {
54      // implementation note: we have to use the following members to
55      // keep track of definitions in different inner classes
56  
57      /** maps OBJ_BLOCK to the method definition of equals() */
58      private final Map mObjBlockEquals = new HashMap();
59  
60      /** the set of OBJ_BLOCKs that contain a definition of hashCode() */
61      private final Set mObjBlockWithHashCode = new HashSet();
62  
63  
64      /** @see Check */
65      public int[] getDefaultTokens()
66      {
67          return new int[] {TokenTypes.METHOD_DEF};
68      }
69  
70      /** @see Check */
71      public void beginTree()
72      {
73          mObjBlockEquals.clear();
74          mObjBlockWithHashCode.clear();
75      }
76  
77      /** @see Check */
78      public void visitToken(DetailAST aAST)
79      {
80          DetailAST modifiers = (DetailAST) aAST.getFirstChild();
81  
82          AST type = modifiers.getNextSibling();
83          AST methodName = type.getNextSibling();
84          DetailAST parameters = (DetailAST) methodName.getNextSibling();
85  
86          if (type.getFirstChild().getType() == TokenTypes.LITERAL_BOOLEAN
87                  && "equals".equals(methodName.getText())
88                  && modifiers.branchContains(TokenTypes.LITERAL_PUBLIC)
89                  && parameters.getChildCount() == 1
90                  && isObjectParam(parameters.getFirstChild())
91                  )
92          {
93              mObjBlockEquals.put(aAST.getParent(), aAST);
94          }
95          else if (type.getFirstChild().getType() == TokenTypes.LITERAL_INT
96                  && "hashCode".equals(methodName.getText())
97                  && modifiers.branchContains(TokenTypes.LITERAL_PUBLIC)
98                  && parameters.getFirstChild() == null) // no params
99          {
100             mObjBlockWithHashCode.add(aAST.getParent());
101         }
102     }
103 
104     /**
105      * Determines if an AST is a formal param of type Object (or subclass).
106      * @param aFirstChild the AST to check
107      * @return true iff aFirstChild is a parameter of an Object type.
108      */
109     private boolean isObjectParam(AST aFirstChild)
110     {
111         final AST modifiers = aFirstChild.getFirstChild();
112         AST type = modifiers.getNextSibling();
113         switch (type.getFirstChild().getType()) {
114             case TokenTypes.LITERAL_BOOLEAN:
115             case TokenTypes.LITERAL_BYTE:
116             case TokenTypes.LITERAL_CHAR:
117             case TokenTypes.LITERAL_DOUBLE:
118             case TokenTypes.LITERAL_FLOAT:
119             case TokenTypes.LITERAL_INT:
120             case TokenTypes.LITERAL_LONG:
121             case TokenTypes.LITERAL_SHORT:
122                 return false;
123             default:
124                 return true;
125         }
126     }
127 
128     /**
129      * @see Check
130      */
131     public void finishTree()
132     {
133         final Set equalsDefs = mObjBlockEquals.keySet();
134         for (Iterator it = equalsDefs.iterator(); it.hasNext();) {
135             Object objBlock = it.next();
136             if (!mObjBlockWithHashCode.contains(objBlock)) {
137                 DetailAST equalsAST = (DetailAST) mObjBlockEquals.get(objBlock);
138                 log(equalsAST.getLineNo(), equalsAST.getColumnNo(),
139                         "equals.noHashCode");
140             }
141         }
142 
143         mObjBlockEquals.clear();
144         mObjBlockWithHashCode.clear();
145     }
146 }