KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > edu > umd > cs > findbugs > detect > Naming


1 /*
2  * FindBugs - Find bugs in Java programs
3  * Copyright (C) 2003,2004 University of Maryland
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 edu.umd.cs.findbugs.detect;
21
22
23 import edu.umd.cs.findbugs.*;
24 import edu.umd.cs.findbugs.ba.AnalysisContext;
25 import edu.umd.cs.findbugs.ba.ClassContext;
26 import edu.umd.cs.findbugs.ba.XFactory;
27 import edu.umd.cs.findbugs.ba.XMethod;
28 import edu.umd.cs.findbugs.visitclass.PreorderVisitor;
29 import java.util.*;
30 import java.util.regex.*;
31 import org.apache.bcel.Repository;
32 import org.apache.bcel.classfile.*;
33 import org.apache.bcel.classfile.Deprecated;
34
35 public class Naming extends PreorderVisitor implements Detector {
36     String JavaDoc baseClassName;
37     boolean classIsPublicOrProtected;
38
39     public static boolean definedIn(JavaClass clazz, XMethod m) {
40         for(Method m2 : clazz.getMethods())
41             if (m.getName().equals(m2.getName()) && m.getSignature().equals(m2.getSignature()) && m.isStatic() == m2.isStatic()) return true;
42         return false;
43     }
44
45     public static boolean confusingMethodNames(XMethod m1, XMethod m2) {
46         if (m1.isStatic() != m2.isStatic()) return false;
47         if (m1.getClassName().equals(m2.getClassName())) return false;
48         
49         if (m1.getName().equalsIgnoreCase(m2.getName())
50                 && !m1.getName().equals(m2.getName())
51                 && m1.getSignature().equals(m2.getSignature())) return true;
52         if (m1.getSignature().equals(m2.getSignature())) return false;
53         if (removePackageNamesFromSignature(m1.getSignature()).equals(removePackageNamesFromSignature(m2.getSignature())))
54                 return true;
55         return false;
56     }
57
58     // map of canonicalName -> trueMethodName
59
HashMap<String JavaDoc, HashSet<String JavaDoc>> canonicalToTrueMapping
60             = new HashMap<String JavaDoc, HashSet<String JavaDoc>>();
61     // map of canonicalName -> Set<XMethod>
62
HashMap<String JavaDoc, HashSet<XMethod>> canonicalToXMethod
63             = new HashMap<String JavaDoc, HashSet<XMethod>>();
64
65     HashSet<String JavaDoc> visited = new HashSet<String JavaDoc>();
66
67     private BugReporter bugReporter;
68
69     public Naming(BugReporter bugReporter) {
70         this.bugReporter = bugReporter;
71     }
72
73     public void visitClassContext(ClassContext classContext) {
74         classContext.getJavaClass().accept(this);
75     }
76
77     private boolean checkSuper(XMethod m, HashSet<XMethod> others) {
78         for (XMethod m2 : others) {
79             try {
80                 if (confusingMethodNames(m, m2)
81                         && Repository.instanceOf(m.getClassName(), m2.getClassName())) {
82                     int priority = HIGH_PRIORITY;
83                     try {
84                     JavaClass clazz = Repository.lookupClass(m.getClassName());
85                     if (definedIn(clazz, m2))
86                         priority+=2;
87                     for(JavaClass i : clazz.getAllInterfaces())
88                         if (definedIn(i, m))
89                             priority+=2;
90                     for(JavaClass s : clazz.getSuperClasses())
91                         if (definedIn(s, m))
92                             priority+=2;
93                     } catch (ClassNotFoundException JavaDoc e) {
94                         priority++;
95                         AnalysisContext.reportMissingClass(e);
96                     }
97                     String JavaDoc pattern = "NM_VERY_CONFUSING";
98                     if (priority == HIGH_PRIORITY && AnalysisContext.currentXFactory().isCalled(m)) priority = NORMAL_PRIORITY;
99                     else if (priority > NORMAL_PRIORITY && m.getSignature().equals(m2.getSignature())) {
100                         pattern = "NM_VERY_CONFUSING_INTENTIONAL";
101                         priority = NORMAL_PRIORITY;
102                     }
103                     XFactory xFactory = AnalysisContext.currentXFactory();
104                     if (xFactory.getDeprecated().contains(m) || xFactory.getDeprecated().contains(m2)) priority++;
105                     
106                     bugReporter.reportBug(new BugInstance(this, pattern, priority)
107                     .addClass(m.getClassName())
108                     .addMethod(m)
109                     .addClass(m2.getClassName())
110                     .addMethod(m2));
111                     return true;
112                 }
113             } catch (ClassNotFoundException JavaDoc e) {
114                 AnalysisContext.reportMissingClass(e);
115             }
116         }
117         return false;
118     }
119
120     private boolean checkNonSuper(XMethod m, HashSet<XMethod> others) {
121         for (XMethod m2 : others) {
122             if (confusingMethodNames(m,m2)) {
123                 bugReporter.reportBug(new BugInstance(this, "NM_CONFUSING", LOW_PRIORITY)
124                         .addClass(m.getClassName())
125                         .addMethod(m)
126                         .addClass(m2.getClassName())
127                         .addMethod(m2));
128                 return true;
129             }
130         }
131         return false;
132     }
133
134
135     public void report() {
136
137     canonicalNameIterator:
138     for (String JavaDoc allSmall : canonicalToTrueMapping.keySet()) {
139         HashSet<String JavaDoc> s = canonicalToTrueMapping.get(allSmall);
140         if (s.size() <= 1)
141             continue;
142         HashSet<XMethod> conflictingMethods = canonicalToXMethod.get(allSmall);
143         for (Iterator<XMethod> j = conflictingMethods.iterator(); j.hasNext();) {
144             if (checkSuper(j.next(), conflictingMethods))
145                 j.remove();
146         }
147         for (XMethod conflictingMethod : conflictingMethods) {
148             if (checkNonSuper(conflictingMethod, conflictingMethods))
149                 continue canonicalNameIterator;
150         }
151     }
152     }
153
154     @Override JavaDoc
155          public void visitJavaClass(JavaClass obj) {
156         if (obj.isInterface()) return;
157         String JavaDoc name = obj.getClassName();
158         if (!visited.add(name)) return;
159         try {
160             JavaClass supers[] = Repository.getSuperClasses(obj);
161             for (JavaClass aSuper : supers) {
162                 visitJavaClass(aSuper);
163             }
164         } catch (ClassNotFoundException JavaDoc e) {
165             // ignore it
166
}
167         super.visitJavaClass(obj);
168     }
169
170     @Override JavaDoc
171          public void visit(JavaClass obj) {
172         String JavaDoc name = obj.getClassName();
173         String JavaDoc[] parts = name.split("[$+.]");
174         baseClassName = parts[parts.length - 1];
175         classIsPublicOrProtected = obj.isPublic() || obj.isProtected();
176         if (baseClassName.length() == 1) return;
177         if(Character.isLetter(baseClassName.charAt(0))
178            && !Character.isUpperCase(baseClassName.charAt(0))
179            && baseClassName.indexOf("_") ==-1
180             )
181             bugReporter.reportBug(new BugInstance(this,
182                 "NM_CLASS_NAMING_CONVENTION",
183                 classIsPublicOrProtected
184                 ? NORMAL_PRIORITY
185                 : LOW_PRIORITY
186                     )
187                     .addClass(this));
188         if (name.endsWith("Exception")
189         && (!obj.getSuperclassName().endsWith("Exception"))
190         && (!obj.getSuperclassName().endsWith("Error"))
191         && (!obj.getSuperclassName().endsWith("Throwable"))) {
192             bugReporter.reportBug(new BugInstance(this,
193                     "NM_CLASS_NOT_EXCEPTION",
194                     NORMAL_PRIORITY )
195                         .addClass(this));
196         }
197             
198         super.visit(obj);
199     }
200
201     @Override JavaDoc
202          public void visit(Field obj) {
203         if (getFieldName().length() == 1) return;
204
205         if (!obj.isFinal()
206             && Character.isLetter(getFieldName().charAt(0))
207             && !Character.isLowerCase(getFieldName().charAt(0))
208             && getFieldName().indexOf("_") == -1
209             && Character.isLetter(getFieldName().charAt(1))
210             && Character.isLowerCase(getFieldName().charAt(1))) {
211             bugReporter.reportBug(new BugInstance(this,
212                 "NM_FIELD_NAMING_CONVENTION",
213                 classIsPublicOrProtected
214                  && (obj.isPublic() || obj.isProtected())
215                 ? NORMAL_PRIORITY
216                 : LOW_PRIORITY)
217                     .addClass(this)
218                     .addVisitedField(this)
219                 );
220         }
221         }
222     private final static Pattern sigType = Pattern.compile("L([^;]*/)?([^/]+;)");
223     private boolean isInnerClass(JavaClass obj) {
224         for(Field f : obj.getFields())
225             if (f.getName().startsWith("this$")) return true;
226         return false;
227     }
228     private boolean markedAsNotUsable(Method obj) {
229         for(Attribute a : obj.getAttributes())
230             if (a instanceof Deprecated JavaDoc) return true;
231         Code code = obj.getCode();
232         if (code == null) return false;
233         byte [] codeBytes = code.getCode();
234         if (codeBytes.length > 1 && codeBytes.length < 10) {
235             int lastOpcode = codeBytes[codeBytes.length-1] & 0xff;
236             if (lastOpcode != ATHROW) return false;
237             for(int b : codeBytes)
238                 if ((b & 0xff) == RETURN) return false;
239             return true;
240             }
241         return false;
242     }
243     @Override JavaDoc
244          public void visit(Method obj) {
245         String JavaDoc mName = getMethodName();
246         if (mName.length() == 1) return;
247         if (Character.isLetter(mName.charAt(0))
248             && !Character.isLowerCase(mName.charAt(0))
249             && Character.isLetter(mName.charAt(1))
250             && Character.isLowerCase(mName.charAt(1))
251             && mName.indexOf("_") == -1 )
252             bugReporter.reportBug(new BugInstance(this,
253                 "NM_METHOD_NAMING_CONVENTION",
254                 classIsPublicOrProtected
255                  && (obj.isPublic() || obj.isProtected())
256                 ? NORMAL_PRIORITY
257                 : LOW_PRIORITY)
258                     .addClassAndMethod(this));
259         String JavaDoc sig = getMethodSig();
260         if (mName.equals(baseClassName) && sig.equals("()V")) {
261             Code code = obj.getCode();
262             if (code != null && !markedAsNotUsable(obj)) {
263                 int priority = NORMAL_PRIORITY;
264                 byte [] codeBytes = code.getCode();
265                 if (codeBytes.length > 1)
266                     priority--;
267                 if (!obj.isPrivate() && getThisClass().isPublic())
268                     priority--;
269                 bugReporter.reportBug( new BugInstance(this, "NM_METHOD_CONSTRUCTOR_CONFUSION", priority).addClassAndMethod(this).lowerPriorityIfDeprecated());
270                 return;
271             }
272         }
273
274         if (obj.isAbstract()) return;
275         if (obj.isPrivate()) return;
276
277         if (mName.equals("equal") && sig.equals("(Ljava/lang/Object;)Z")) {
278             bugReporter.reportBug(new BugInstance(this, "NM_BAD_EQUAL", HIGH_PRIORITY)
279                     .addClassAndMethod(this).lowerPriorityIfDeprecated());
280             return;
281         }
282         if (mName.equals("hashcode") && sig.equals("()I")) {
283             bugReporter.reportBug(new BugInstance(this, "NM_LCASE_HASHCODE", HIGH_PRIORITY)
284                     .addClassAndMethod(this).lowerPriorityIfDeprecated());
285             return;
286         }
287         if (mName.equals("tostring") && sig.equals("()Ljava/lang/String;")) {
288             bugReporter.reportBug(new BugInstance(this, "NM_LCASE_TOSTRING", HIGH_PRIORITY)
289                     .addClassAndMethod(this).lowerPriorityIfDeprecated());
290             return;
291         }
292
293
294         if (obj.isPrivate()
295                 || obj.isStatic()
296                 || mName.equals("<init>")
297         )
298             return;
299
300         String JavaDoc trueName = mName + sig;
301         String JavaDoc sig2 = removePackageNamesFromSignature(sig);
302         String JavaDoc allSmall = mName.toLowerCase() + sig2;
303     
304
305         XMethod xm = XFactory.createXMethod(this);
306         {
307             HashSet<String JavaDoc> s = canonicalToTrueMapping.get(allSmall);
308             if (s == null) {
309                 s = new HashSet<String JavaDoc>();
310                 canonicalToTrueMapping.put(allSmall, s);
311             }
312             s.add(trueName);
313         }
314         {
315             HashSet<XMethod> s = canonicalToXMethod.get(allSmall);
316             if (s == null) {
317                 s = new HashSet<XMethod>();
318                 canonicalToXMethod.put(allSmall, s);
319             }
320             s.add(xm);
321         }
322
323     }
324
325     private static String JavaDoc removePackageNamesFromSignature(String JavaDoc sig) {
326         int end = sig.indexOf(")");
327         Matcher m = sigType.matcher(sig.substring(0,end));
328         return m.replaceAll("L$2") + sig.substring(end);
329     }
330
331
332 }
333
Popular Tags