KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > net > sf > clirr > core > internal > checks > MethodSetCheck


1 //////////////////////////////////////////////////////////////////////////////
2
// Clirr: compares two versions of a java library for binary compatibility
3
// Copyright (C) 2003 - 2005 Lars Kühne
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 net.sf.clirr.core.internal.checks;
21
22 import net.sf.clirr.core.ApiDifference;
23 import net.sf.clirr.core.Message;
24 import net.sf.clirr.core.Severity;
25 import net.sf.clirr.core.ScopeSelector;
26 import net.sf.clirr.core.internal.AbstractDiffReporter;
27 import net.sf.clirr.core.internal.ApiDiffDispatcher;
28 import net.sf.clirr.core.internal.ClassChangeCheck;
29 import net.sf.clirr.core.internal.CoIterator;
30 import net.sf.clirr.core.spi.JavaType;
31 import net.sf.clirr.core.spi.Method;
32 import net.sf.clirr.core.spi.Scope;
33
34 import java.util.ArrayList JavaDoc;
35 import java.util.HashMap JavaDoc;
36 import java.util.Iterator JavaDoc;
37 import java.util.List JavaDoc;
38 import java.util.Map JavaDoc;
39
40 /**
41  * Checks the methods of a class.
42  *
43  * @author lkuehne
44  */

45 public class MethodSetCheck
46     extends AbstractDiffReporter
47     implements ClassChangeCheck
48 {
49     private static final Message MSG_METHOD_NOW_IN_SUPERCLASS = new Message(7000);
50     private static final Message MSG_METHOD_NOW_IN_INTERFACE = new Message(7001);
51     private static final Message MSG_METHOD_REMOVED = new Message(7002);
52     private static final Message MSG_METHOD_OVERRIDE_REMOVED = new Message(7003);
53     private static final Message MSG_METHOD_ARGCOUNT_CHANGED = new Message(7004);
54     private static final Message MSG_METHOD_PARAMTYPE_CHANGED = new Message(7005);
55     private static final Message MSG_METHOD_RETURNTYPE_CHANGED = new Message(7006);
56     private static final Message MSG_METHOD_DEPRECATED = new Message(7007);
57     private static final Message MSG_METHOD_UNDEPRECATED = new Message(7008);
58     private static final Message MSG_METHOD_LESS_ACCESSIBLE = new Message(7009);
59     private static final Message MSG_METHOD_MORE_ACCESSIBLE = new Message(7010);
60     private static final Message MSG_METHOD_ADDED = new Message(7011);
61     private static final Message MSG_METHOD_ADDED_TO_INTERFACE = new Message(7012);
62     private static final Message MSG_ABSTRACT_METHOD_ADDED = new Message(7013);
63     private static final Message MSG_METHOD_NOW_FINAL = new Message(7014);
64     private static final Message MSG_METHOD_NOW_NONFINAL = new Message(7015);
65
66     private ScopeSelector scopeSelector;
67
68     /**
69      * Instantiates the check.
70      *
71      * @param dispatcher the dispatcher where detected differences shoudl be reported.
72      * @param scopeSelector defines the scopes to look at when searching for differences.
73      */

74     public MethodSetCheck(ApiDiffDispatcher dispatcher, ScopeSelector scopeSelector)
75     {
76         super(dispatcher);
77         this.scopeSelector = scopeSelector;
78     }
79
80     public final boolean check(JavaType compatBaseline, JavaType currentVersion)
81     {
82         // Dont't report method problems when gender has changed, as
83
// really the whole API is a pile of crap then - let GenderChange check
84
// do it's job, and that's it
85
if (compatBaseline.isInterface() ^ currentVersion.isInterface())
86         {
87             return true;
88         }
89
90         Map JavaDoc bNameToMethod = buildNameToMethodMap(compatBaseline);
91         Map JavaDoc cNameToMethod = buildNameToMethodMap(currentVersion);
92
93         CoIterator iter = new CoIterator(null, bNameToMethod.keySet(), cNameToMethod.keySet());
94
95         while (iter.hasNext())
96         {
97             iter.next();
98
99             String JavaDoc baselineMethodName = (String JavaDoc) iter.getLeft();
100             String JavaDoc currentMethodName = (String JavaDoc) iter.getRight();
101
102             if (baselineMethodName == null)
103             {
104                 // a new method name has been added in the new version
105
List JavaDoc currentMethods = (List JavaDoc) cNameToMethod.get(currentMethodName);
106                 reportMethodsAdded(currentVersion, currentMethods);
107             }
108             else if (currentMethodName == null)
109             {
110                 // all methods with name x have been removed from the old version
111
List JavaDoc baselineMethods = (List JavaDoc) bNameToMethod.get(baselineMethodName);
112                 reportMethodsRemoved(compatBaseline, baselineMethods, currentVersion);
113             }
114             else
115             {
116                 // assert baselineMethodName equals currentMethodName
117

118                 List JavaDoc baselineMethods = (List JavaDoc) bNameToMethod.get(baselineMethodName);
119                 List JavaDoc currentMethods = (List JavaDoc) cNameToMethod.get(currentMethodName);
120
121                 filterSoftMatchedMethods(
122                     compatBaseline, baselineMethods,
123                     currentVersion, currentMethods);
124
125                 filterChangedMethods(
126                     baselineMethodName,
127                     compatBaseline, baselineMethods,
128                     currentVersion, currentMethods);
129
130                 // if any methods are left, they have no matching method in
131
// the other version, so report as removed or added respectively.
132

133                 if (!baselineMethods.isEmpty())
134                 {
135                     reportMethodsRemoved(compatBaseline, baselineMethods, currentVersion);
136                 }
137
138                 if (!currentMethods.isEmpty())
139                 {
140                     reportMethodsAdded(currentVersion, currentMethods);
141                 }
142             }
143         }
144
145         return true;
146     }
147
148     /**
149      * Given a list of old and new methods for a particular method name,
150      * find the (old, new) method pairs which have identical argument lists.
151      * <p>
152      * For these:
153      * <ul>
154      * <li>report on changes in accessibility, return type, etc
155      * <li>remove from the list
156      * </ul>
157      *
158      * On return from this method, the old and new method lists contain only
159      * methods whose argument lists have changed between versions [or possibly,
160      * methods which have been deleted while one or more new methods of the
161      * same name have been added, depending on how you view it]. All other
162      * situations have been dealt with.
163      * <p>
164      * Note that one or both method lists may be empty on return from
165      * this method.
166      */

167     private void filterSoftMatchedMethods(
168             JavaType compatBaseline,
169             List JavaDoc baselineMethods,
170             JavaType currentVersion,
171             List JavaDoc currentMethods)
172     {
173         for (Iterator bIter = baselineMethods.iterator(); bIter.hasNext();)
174         {
175             Method bMethod = (Method) bIter.next();
176
177             for (Iterator cIter = currentMethods.iterator(); cIter.hasNext();)
178             {
179                 Method cMethod = (Method) cIter.next();
180
181                 if (isSoftMatch(bMethod, cMethod))
182                 {
183                     check(compatBaseline, bMethod, cMethod);
184                     bIter.remove();
185                     cIter.remove();
186                     break;
187                 }
188             }
189         }
190     }
191
192     /**
193      * Two methods are a "soft" match if they have the same name and argument
194      * list. No two methods on the same class are ever a "soft match" for
195      * each other, because the compiler requires distinct parameter lists for
196      * overloaded methods. This also implies that for a given method on an "old"
197      * class version, there are either zero or one "soft matches" on the new
198      * version.
199      * <p>
200      * However a "soft match" is not sufficient to ensure binary compatibility.
201      * A change in the method return type will result in a link error when used
202      * with code compiled against the previous version of the class.
203      * <p>
204      * There may also be other differences between methods that are regarded
205      * as "soft matches": the exceptions thrown, the deprecation status of the
206      * methods, their accessibility, etc.
207      */

208     private boolean isSoftMatch(Method oldMethod, Method newMethod)
209     {
210         String JavaDoc oldName = oldMethod.getName();
211         String JavaDoc newName = newMethod.getName();
212
213         if (!oldName.equals(newName))
214         {
215             return false;
216         }
217
218         StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
219         appendHumanReadableArgTypeList(oldMethod, buf);
220         String JavaDoc oldArgs = buf.toString();
221
222         buf.setLength(0);
223         appendHumanReadableArgTypeList(newMethod, buf);
224         String JavaDoc newArgs = buf.toString();
225
226         return (oldArgs.equals(newArgs));
227     }
228
229     /**
230      * For each method in the baselineMethods list, find the "best match"
231      * in the currentMethods list, report the changes between this method
232      * pair, then remove both methods from the lists.
233      * <p>
234      * On return, at least one of the method lists will be empty.
235      */

236     private void filterChangedMethods(
237             String JavaDoc methodName,
238             JavaType compatBaseline,
239             List JavaDoc baselineMethods,
240             JavaType currentVersion,
241             List JavaDoc currentMethods)
242     {
243         // ok, we now have to deal with the tricky cases, where it is not
244
// immediately obvious which old methods correspond to which new
245
// methods.
246
//
247
// Here we build a similarity table, i.e. for new method i and old
248
// method j we have number that charaterizes how similar the method
249
// signatures are (0 means equal, higher number means more different)
250

251         while (!baselineMethods.isEmpty() && !currentMethods.isEmpty())
252         {
253             int[][] similarityTable = buildSimilarityTable(baselineMethods, currentMethods);
254
255             int min = Integer.MAX_VALUE;
256             int iMin = baselineMethods.size();
257             int jMin = currentMethods.size();
258             for (int i = 0; i < baselineMethods.size(); i++)
259             {
260                 for (int j = 0; j < currentMethods.size(); j++)
261                 {
262                     final int tableEntry = similarityTable[i][j];
263                     if (tableEntry < min)
264                     {
265                         min = tableEntry;
266                         iMin = i;
267                         jMin = j;
268                     }
269                 }
270             }
271             Method iMethod = (Method) baselineMethods.remove(iMin);
272             Method jMethod = (Method) currentMethods.remove(jMin);
273             check(compatBaseline, iMethod, jMethod);
274         }
275     }
276
277     private int[][] buildSimilarityTable(List JavaDoc baselineMethods, List JavaDoc currentMethods)
278     {
279         int[][] similarityTable = new int[baselineMethods.size()][currentMethods.size()];
280         for (int i = 0; i < baselineMethods.size(); i++)
281         {
282             for (int j = 0; j < currentMethods.size(); j++)
283             {
284                 final Method iMethod = (Method) baselineMethods.get(i);
285                 final Method jMethod = (Method) currentMethods.get(j);
286                 similarityTable[i][j] = distance(iMethod, jMethod);
287             }
288         }
289         return similarityTable;
290     }
291
292     private int distance(Method m1, Method m2)
293     {
294         final JavaType[] m1Args = m1.getArgumentTypes();
295         final JavaType[] m2Args = m2.getArgumentTypes();
296
297         if (m1Args.length != m2Args.length)
298         {
299             return 1000 * Math.abs(m1Args.length - m2Args.length);
300         }
301
302         int retVal = 0;
303         for (int i = 0; i < m1Args.length; i++)
304         {
305             if (!m1Args[i].toString().equals(m2Args[i].toString()))
306             {
307                 retVal += 1;
308             }
309         }
310         return retVal;
311     }
312
313     /**
314      * Searches the class hierarchy for a method that has a certain signature.
315      * @param methodSignature the sig we're looking for
316      * @param clazz class where search starts
317      * @return class name of a superclass of clazz, might be null
318      */

319     private String JavaDoc findSuperClassWithSignature(String JavaDoc methodSignature, JavaType clazz)
320     {
321         final JavaType[] superClasses = clazz.getSuperClasses();
322         for (int i = 0; i < superClasses.length; i++)
323         {
324             JavaType superClass = superClasses[i];
325             final Method[] superMethods = superClass.getMethods();
326             for (int j = 0; j < superMethods.length; j++)
327             {
328                 Method superMethod = superMethods[j];
329                 final String JavaDoc superMethodSignature = getMethodId(superClass, superMethod);
330                 if (methodSignature.equals(superMethodSignature))
331                 {
332                     return superClass.getName();
333                 }
334             }
335
336         }
337         return null;
338     }
339
340     /**
341      * Searches the class hierarchy for a method that has a certtain signature.
342      * @param methodSignature the sig we're looking for
343      * @param clazz class where search starts
344      * @return class name of a superinterface of clazz, might be null
345      */

346     private String JavaDoc findSuperInterfaceWithSignature(String JavaDoc methodSignature, JavaType clazz)
347     {
348         final JavaType[] superClasses = clazz.getAllInterfaces();
349         for (int i = 0; i < superClasses.length; i++)
350         {
351             JavaType superClass = superClasses[i];
352             final Method[] superMethods = superClass.getMethods();
353             for (int j = 0; j < superMethods.length; j++)
354             {
355                 Method superMethod = superMethods[j];
356                 final String JavaDoc superMethodSignature = getMethodId(superClass, superMethod);
357                 if (methodSignature.equals(superMethodSignature))
358                 {
359                     return superClass.getName();
360                 }
361             }
362
363         }
364         return null;
365     }
366
367     /**
368      * Given a list of methods, report each one as being removed.
369      */

370     private void reportMethodsRemoved(
371             JavaType baselineClass,
372             List JavaDoc baselineMethods,
373             JavaType currentClass)
374     {
375         for (Iterator i = baselineMethods.iterator(); i.hasNext();)
376         {
377             Method method = (Method) i.next();
378             reportMethodRemoved(baselineClass, method, currentClass);
379         }
380     }
381
382     /**
383      * Report that a method has been removed from a class.
384      * @param oldClass the class where the method was available
385      * @param oldMethod the method that has been removed
386      * @param currentClass the superclass where the method is now available, might be null
387      */

388     private void reportMethodRemoved(
389             JavaType oldClass,
390             Method oldMethod,
391             JavaType currentClass)
392     {
393         if (!scopeSelector.isSelected(oldMethod))
394         {
395             return;
396         }
397
398         String JavaDoc signature = getMethodId(oldClass, oldMethod);
399
400         String JavaDoc oldBaseClassForMethod = findSuperClassWithSignature(signature, oldClass);
401         String JavaDoc oldInterfaceForMethod = findSuperInterfaceWithSignature(signature, oldClass);
402
403         String JavaDoc newBaseClassForMethod = findSuperClassWithSignature(signature, currentClass);
404         String JavaDoc newInterfaceForMethod = findSuperInterfaceWithSignature(signature, currentClass);
405
406         boolean oldInheritedMethod = (oldBaseClassForMethod != null) || (oldInterfaceForMethod != null);
407         boolean newInheritedMethod = (newBaseClassForMethod != null) || (newInterfaceForMethod != null);
408
409         if (oldInheritedMethod && newInheritedMethod)
410         {
411             // Previously, this method overrode an inherited definition.
412
// The current version of the class doesn't have this
413
// method, but a parent class or interface still does, so this
414
// does not cause an incompatibility.
415
fireDiff(MSG_METHOD_OVERRIDE_REMOVED,
416                     Severity.INFO,
417                     oldClass, oldMethod, null);
418         }
419         else if (oldInheritedMethod)
420         {
421             // Previously, this method override an inherited definition.
422
// It isn't present in the current class, though, and neither is
423
// it present in the new class' ancestors. Best to just
424
// report it as removed...
425
fireDiff(MSG_METHOD_REMOVED,
426                     getSeverity(oldClass, oldMethod, Severity.ERROR),
427                     oldClass, oldMethod, null);
428         }
429         else if (newBaseClassForMethod != null)
430         {
431             // Previously, this method didn't override anything. The current
432
// version of this class doesn't have this method any more,
433
// but an ancestor class now *does*. This is an instance
434
// of the pull-up refactoring pattern, where a method is moved
435
// to an ancestor class.
436
fireDiff(MSG_METHOD_NOW_IN_SUPERCLASS,
437                     Severity.INFO, oldClass, oldMethod,
438                     new String JavaDoc[] {newBaseClassForMethod});
439         }
440         else if (newInterfaceForMethod != null)
441         {
442             // Previously, this method didn't override anything. The current
443
// version of this class doesn't have this method any more,
444
// but one of the implemented interfaces now *does*. This is an
445
// instance of the pull-up refactoring pattern, where a method is
446
// moved to an interface.
447
fireDiff(MSG_METHOD_NOW_IN_INTERFACE,
448                     Severity.INFO, oldClass, oldMethod,
449                     new String JavaDoc[] {newInterfaceForMethod});
450         }
451         else
452         {
453             // This method wasn't anything special in the old class, and
454
// it isn't present in the new class either directly or via
455
// inheritance.
456
fireDiff(MSG_METHOD_REMOVED,
457                     getSeverity(oldClass, oldMethod, Severity.ERROR),
458                     oldClass, oldMethod, null);
459         }
460     }
461
462     /**
463      * Given a list of methods, report each one as being added.
464      */

465     private void reportMethodsAdded(
466             JavaType currentClass,
467             List JavaDoc currentMethods)
468     {
469         for (Iterator i = currentMethods.iterator(); i.hasNext();)
470         {
471             Method method = (Method) i.next();
472             reportMethodAdded(currentClass, method);
473         }
474     }
475
476     /**
477      * Report that a method has been added to a class.
478      */

479     private void reportMethodAdded(JavaType newClass, Method newMethod)
480     {
481         if (!scopeSelector.isSelected(newMethod))
482         {
483             return;
484         }
485
486         if (newClass.isInterface())
487         {
488             // TODO: this is not an incompatibility if the new method
489
// actually already exists on a parent interface of the
490
// old interface. In that case, any class implementing the
491
// old version of this interface must already have an
492
// implementation of this method. See bugtracker #961217
493
fireDiff(MSG_METHOD_ADDED_TO_INTERFACE,
494                     getSeverity(newClass, newMethod, Severity.ERROR),
495                     newClass, newMethod, null);
496         }
497         else if (newMethod.isAbstract())
498         {
499             // TODO: this is not an incompatibility if the new method
500
// actually already exists on a parent interface of the
501
// old interface and was abstract. In that case, any class
502
// extending the old version of this class must already
503
// have an implementation of this method.
504
//
505
// Note that abstract methods can never be package or private
506
// scope, so we don't need to use the getSeverity method.
507
fireDiff(MSG_ABSTRACT_METHOD_ADDED,
508                     Severity.ERROR, newClass, newMethod, null);
509         }
510         else
511         {
512             // TODO:
513
//. (a) check whether this method exists on a parent of the
514
// new class. If so, indicate that this new method is overriding
515
// some inherited method.
516
// (b) if not a, then check whether this method exists on a parent
517
// of the old class. If so, then report that the method has
518
// been moved from the parent to the child class. This is
519
// potentially useful info for the user.
520
//
521
// See bugtracker #959225
522
fireDiff(MSG_METHOD_ADDED,
523                     Severity.INFO, newClass, newMethod, null);
524         }
525     }
526
527     /**
528      * Builds a map from a method name to a List of methods.
529      */

530     private Map JavaDoc buildNameToMethodMap(JavaType clazz)
531     {
532         Method[] methods = clazz.getMethods();
533         Map JavaDoc retVal = new HashMap JavaDoc();
534         for (int i = 0; i < methods.length; i++)
535         {
536             Method method = methods[i];
537
538             final String JavaDoc name = method.getName();
539             List JavaDoc set = (List JavaDoc) retVal.get(name);
540             if (set == null)
541             {
542                 set = new ArrayList JavaDoc();
543                 retVal.put(name, set);
544             }
545             set.add(method);
546         }
547         return retVal;
548     }
549
550     private void check(JavaType compatBaseline, Method baselineMethod, Method currentMethod)
551     {
552         if (!scopeSelector.isSelected(baselineMethod) && !scopeSelector.isSelected(currentMethod))
553         {
554             return;
555         }
556
557         checkParameterTypes(compatBaseline, baselineMethod, currentMethod);
558         checkReturnType(compatBaseline, baselineMethod, currentMethod);
559         checkDeclaredExceptions(compatBaseline, baselineMethod, currentMethod);
560         checkDeprecated(compatBaseline, baselineMethod, currentMethod);
561         checkVisibility(compatBaseline, baselineMethod, currentMethod);
562         checkFinal(compatBaseline, baselineMethod, currentMethod);
563     }
564
565     private void checkParameterTypes(JavaType compatBaseline, Method baselineMethod, Method currentMethod)
566     {
567         JavaType[] bArgs = baselineMethod.getArgumentTypes();
568         JavaType[] cArgs = currentMethod.getArgumentTypes();
569
570         if (bArgs.length != cArgs.length)
571         {
572             fireDiff(MSG_METHOD_ARGCOUNT_CHANGED,
573                     getSeverity(compatBaseline, baselineMethod, Severity.ERROR),
574                     compatBaseline, baselineMethod, null);
575             return;
576         }
577
578         //System.out.println("baselineMethod = " + getMethodId(compatBaseline, baselineMethod));
579
for (int i = 0; i < bArgs.length; i++)
580         {
581             JavaType bArg = bArgs[i];
582             JavaType cArg = cArgs[i];
583
584             if (bArg.getName().equals(cArg.getName()))
585             {
586                 continue;
587             }
588
589             // TODO: Check assignability...
590
String JavaDoc[] args =
591             {
592                 "" + (i + 1),
593                 cArg.toString()
594             };
595             fireDiff(MSG_METHOD_PARAMTYPE_CHANGED,
596                     getSeverity(compatBaseline, baselineMethod, Severity.ERROR),
597                     compatBaseline, baselineMethod, args);
598         }
599     }
600
601     private void checkReturnType(JavaType compatBaseline, Method baselineMethod, Method currentMethod)
602     {
603         JavaType bReturnType = baselineMethod.getReturnType();
604         JavaType cReturnType = currentMethod.getReturnType();
605
606         // TODO: Check assignability. If the new return type is
607
// assignable to the old type, then the code is source-code
608
// compatible even when binary-incompatible.
609
if (!bReturnType.toString().equals(cReturnType.toString()))
610         {
611             fireDiff(MSG_METHOD_RETURNTYPE_CHANGED,
612                     getSeverity(compatBaseline, baselineMethod, Severity.ERROR),
613                     compatBaseline, baselineMethod,
614                     new String JavaDoc[] {cReturnType.toString()});
615         }
616     }
617
618     private void checkDeclaredExceptions(
619             JavaType compatBaseline,
620             Method baselineMethod, Method currentMethod)
621     {
622         // TODO
623
}
624
625     private void checkDeprecated(
626             JavaType compatBaseline,
627             Method baselineMethod, Method currentMethod)
628     {
629         boolean bIsDeprecated = baselineMethod.isDeprecated();
630         boolean cIsDeprecated = currentMethod.isDeprecated();
631
632         if (bIsDeprecated && !cIsDeprecated)
633         {
634             fireDiff(MSG_METHOD_UNDEPRECATED,
635                     Severity.INFO, compatBaseline, baselineMethod, null);
636         }
637         else if (!bIsDeprecated && cIsDeprecated)
638         {
639             fireDiff(MSG_METHOD_DEPRECATED,
640                     Severity.INFO, compatBaseline, baselineMethod, null);
641         }
642     }
643
644     /**
645      * Report changes in the declared accessibility of a method
646      * (public/protected/etc).
647      */

648     private void checkVisibility(JavaType compatBaseline, Method baselineMethod, Method currentMethod)
649     {
650         Scope bScope = baselineMethod.getEffectiveScope();
651         Scope cScope = currentMethod.getEffectiveScope();
652
653         if (cScope.isLessVisibleThan(bScope))
654         {
655             String JavaDoc[] args = {bScope.getDesc(), cScope.getDesc()};
656             fireDiff(MSG_METHOD_LESS_ACCESSIBLE,
657                     getSeverity(compatBaseline, baselineMethod, Severity.ERROR),
658                     compatBaseline, baselineMethod, args);
659         }
660         else if (cScope.isMoreVisibleThan(bScope))
661         {
662             String JavaDoc[] args = {bScope.getDesc(), cScope.getDesc()};
663             fireDiff(MSG_METHOD_MORE_ACCESSIBLE,
664                     Severity.INFO, compatBaseline, baselineMethod, args);
665         }
666     }
667
668     private void checkFinal(
669             JavaType compatBaseline,
670             Method baselineMethod, Method currentMethod)
671     {
672         boolean bIsFinal = baselineMethod.isFinal();
673         boolean cIsFinal = currentMethod.isFinal();
674
675         if (bIsFinal && !cIsFinal)
676         {
677             fireDiff(MSG_METHOD_NOW_NONFINAL,
678                     Severity.INFO, compatBaseline, baselineMethod, null);
679         }
680         else if (!bIsFinal && cIsFinal)
681         {
682             fireDiff(MSG_METHOD_NOW_FINAL,
683                     Severity.ERROR, compatBaseline, baselineMethod, null);
684         }
685     }
686
687     /**
688      * Creates a human readable String that is similar to the method signature
689      * and identifies the method within a class.
690      * @param clazz the container of the method
691      * @param method the method to identify.
692      * @return a human readable id, for example "public void print(java.lang.String)"
693      */

694     private String JavaDoc getMethodId(JavaType clazz, Method method)
695     {
696         StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
697
698         final String JavaDoc scopeDecl = method.getDeclaredScope().getDecl();
699         if (scopeDecl.length() > 0)
700         {
701             buf.append(scopeDecl);
702             buf.append(" ");
703         }
704
705         String JavaDoc name = method.getName();
706         if ("<init>".equals(name))
707         {
708             final String JavaDoc className = clazz.getName();
709             int idx = className.lastIndexOf('.');
710             name = className.substring(idx + 1);
711         }
712         else
713         {
714             buf.append(method.getReturnType());
715             buf.append(' ');
716         }
717         buf.append(name);
718         buf.append('(');
719         appendHumanReadableArgTypeList(method, buf);
720         buf.append(')');
721         return buf.toString();
722     }
723
724     private void appendHumanReadableArgTypeList(Method method, StringBuffer JavaDoc buf)
725     {
726         JavaType[] argTypes = method.getArgumentTypes();
727         String JavaDoc argSeparator = "";
728         for (int i = 0; i < argTypes.length; i++)
729         {
730             buf.append(argSeparator);
731             buf.append(argTypes[i].getName());
732             argSeparator = ", ";
733         }
734     }
735
736     private void fireDiff(Message msg, Severity severity, JavaType clazz, Method method, String JavaDoc[] args)
737     {
738         final String JavaDoc className = clazz.getName();
739         final ApiDifference diff =
740             new ApiDifference(
741                 msg, severity, className, getMethodId(clazz, method), null, args);
742         getApiDiffDispatcher().fireDiff(diff);
743     }
744
745 }
746
Popular Tags