KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > puppycrawl > tools > checkstyle > checks > coding > EqualsHashCodeCheck


1 ////////////////////////////////////////////////////////////////////////////////
2
// checkstyle: Checks Java source code for adherence to a set of rules.
3
// Copyright (C) 2001-2005 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.coding;
21
22 import java.util.HashMap JavaDoc;
23 import java.util.HashSet JavaDoc;
24 import java.util.Iterator JavaDoc;
25 import java.util.Map JavaDoc;
26 import java.util.Set JavaDoc;
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 JavaDoc mObjBlockEquals = new HashMap JavaDoc();
59
60     /** the set of OBJ_BLOCKs that contain a definition of hashCode() */
61     private final Set JavaDoc mObjBlockWithHashCode = new HashSet JavaDoc();
62
63
64     /** {@inheritDoc} */
65     public int[] getDefaultTokens()
66     {
67         return new int[] {TokenTypes.METHOD_DEF};
68     }
69
70     /** {@inheritDoc} */
71     public void beginTree(DetailAST aRootAST)
72     {
73         mObjBlockEquals.clear();
74         mObjBlockWithHashCode.clear();
75     }
76
77     /** {@inheritDoc} */
78     public void visitToken(DetailAST aAST)
79     {
80         final DetailAST modifiers = (DetailAST) aAST.getFirstChild();
81         final AST type = aAST.findFirstToken(TokenTypes.TYPE);
82         final AST methodName = aAST.findFirstToken(TokenTypes.IDENT);
83         final DetailAST parameters = aAST.findFirstToken(TokenTypes.PARAMETERS);
84
85         if ((type.getFirstChild().getType() == TokenTypes.LITERAL_BOOLEAN)
86                 && "equals".equals(methodName.getText())
87                 && modifiers.branchContains(TokenTypes.LITERAL_PUBLIC)
88                 && (parameters.getChildCount() == 1)
89                 && isObjectParam(parameters.getFirstChild())
90             )
91         {
92             mObjBlockEquals.put(aAST.getParent(), aAST);
93         }
94         else if ((type.getFirstChild().getType() == TokenTypes.LITERAL_INT)
95                 && "hashCode".equals(methodName.getText())
96                 && modifiers.branchContains(TokenTypes.LITERAL_PUBLIC)
97                 && (parameters.getFirstChild() == null)) // no params
98
{
99             mObjBlockWithHashCode.add(aAST.getParent());
100         }
101     }
102
103     /**
104      * Determines if an AST is a formal param of type Object (or subclass).
105      * @param aFirstChild the AST to check
106      * @return true iff aFirstChild is a parameter of an Object type.
107      */

108     private boolean isObjectParam(AST aFirstChild)
109     {
110         final AST modifiers = aFirstChild.getFirstChild();
111         final AST type = modifiers.getNextSibling();
112         switch (type.getFirstChild().getType()) {
113         case TokenTypes.LITERAL_BOOLEAN:
114         case TokenTypes.LITERAL_BYTE:
115         case TokenTypes.LITERAL_CHAR:
116         case TokenTypes.LITERAL_DOUBLE:
117         case TokenTypes.LITERAL_FLOAT:
118         case TokenTypes.LITERAL_INT:
119         case TokenTypes.LITERAL_LONG:
120         case TokenTypes.LITERAL_SHORT:
121             return false;
122         default:
123             return true;
124         }
125     }
126
127     /**
128      * {@inheritDoc}
129      */

130     public void finishTree(DetailAST aRootAST)
131     {
132         final Set JavaDoc equalsDefs = mObjBlockEquals.keySet();
133         for (final Iterator JavaDoc it = equalsDefs.iterator(); it.hasNext();) {
134             final Object JavaDoc objBlock = it.next();
135             if (!mObjBlockWithHashCode.contains(objBlock)) {
136                 final DetailAST equalsAST =
137                     (DetailAST) mObjBlockEquals.get(objBlock);
138                 log(equalsAST.getLineNo(), equalsAST.getColumnNo(),
139                         "equals.noHashCode");
140             }
141         }
142
143         mObjBlockEquals.clear();
144         mObjBlockWithHashCode.clear();
145     }
146 }
147
Popular Tags