Source Code Cross Referenced for MethodSetCheck.java in  » Code-Analyzer » Clirr » net » sf » clirr » core » internal » checks » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » Code Analyzer » Clirr » net.sf.clirr.core.internal.checks 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


001:        //////////////////////////////////////////////////////////////////////////////
002:        // Clirr: compares two versions of a java library for binary compatibility
003:        // Copyright (C) 2003 - 2005  Lars Kühne
004:        //
005:        // This library is free software; you can redistribute it and/or
006:        // modify it under the terms of the GNU Lesser General Public
007:        // License as published by the Free Software Foundation; either
008:        // version 2.1 of the License, or (at your option) any later version.
009:        //
010:        // This library is distributed in the hope that it will be useful,
011:        // but WITHOUT ANY WARRANTY; without even the implied warranty of
012:        // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
013:        // Lesser General Public License for more details.
014:        //
015:        // You should have received a copy of the GNU Lesser General Public
016:        // License along with this library; if not, write to the Free Software
017:        // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
018:        //////////////////////////////////////////////////////////////////////////////
019:
020:        package net.sf.clirr.core.internal.checks;
021:
022:        import net.sf.clirr.core.ApiDifference;
023:        import net.sf.clirr.core.Message;
024:        import net.sf.clirr.core.Severity;
025:        import net.sf.clirr.core.ScopeSelector;
026:        import net.sf.clirr.core.internal.AbstractDiffReporter;
027:        import net.sf.clirr.core.internal.ApiDiffDispatcher;
028:        import net.sf.clirr.core.internal.ClassChangeCheck;
029:        import net.sf.clirr.core.internal.CoIterator;
030:        import net.sf.clirr.core.spi.JavaType;
031:        import net.sf.clirr.core.spi.Method;
032:        import net.sf.clirr.core.spi.Scope;
033:
034:        import java.util.ArrayList;
035:        import java.util.HashMap;
036:        import java.util.Iterator;
037:        import java.util.List;
038:        import java.util.Map;
039:
040:        /**
041:         * Checks the methods of a class.
042:         *
043:         * @author lkuehne
044:         */
045:        public class MethodSetCheck extends AbstractDiffReporter implements 
046:                ClassChangeCheck {
047:            private static final Message MSG_METHOD_NOW_IN_SUPERCLASS = new Message(
048:                    7000);
049:            private static final Message MSG_METHOD_NOW_IN_INTERFACE = new Message(
050:                    7001);
051:            private static final Message MSG_METHOD_REMOVED = new Message(7002);
052:            private static final Message MSG_METHOD_OVERRIDE_REMOVED = new Message(
053:                    7003);
054:            private static final Message MSG_METHOD_ARGCOUNT_CHANGED = new Message(
055:                    7004);
056:            private static final Message MSG_METHOD_PARAMTYPE_CHANGED = new Message(
057:                    7005);
058:            private static final Message MSG_METHOD_RETURNTYPE_CHANGED = new Message(
059:                    7006);
060:            private static final Message MSG_METHOD_DEPRECATED = new Message(
061:                    7007);
062:            private static final Message MSG_METHOD_UNDEPRECATED = new Message(
063:                    7008);
064:            private static final Message MSG_METHOD_LESS_ACCESSIBLE = new Message(
065:                    7009);
066:            private static final Message MSG_METHOD_MORE_ACCESSIBLE = new Message(
067:                    7010);
068:            private static final Message MSG_METHOD_ADDED = new Message(7011);
069:            private static final Message MSG_METHOD_ADDED_TO_INTERFACE = new Message(
070:                    7012);
071:            private static final Message MSG_ABSTRACT_METHOD_ADDED = new Message(
072:                    7013);
073:            private static final Message MSG_METHOD_NOW_FINAL = new Message(
074:                    7014);
075:            private static final Message MSG_METHOD_NOW_NONFINAL = new Message(
076:                    7015);
077:
078:            private ScopeSelector scopeSelector;
079:
080:            /**
081:             * Instantiates the check.
082:             *
083:             * @param dispatcher the dispatcher where detected differences shoudl be reported.
084:             * @param scopeSelector defines the scopes to look at when searching for differences.
085:             */
086:            public MethodSetCheck(ApiDiffDispatcher dispatcher,
087:                    ScopeSelector scopeSelector) {
088:                super (dispatcher);
089:                this .scopeSelector = scopeSelector;
090:            }
091:
092:            public final boolean check(JavaType compatBaseline,
093:                    JavaType currentVersion) {
094:                // Dont't report method problems when gender has changed, as
095:                // really the whole API is a pile of crap then - let GenderChange check
096:                // do it's job, and that's it
097:                if (compatBaseline.isInterface() ^ currentVersion.isInterface()) {
098:                    return true;
099:                }
100:
101:                Map bNameToMethod = buildNameToMethodMap(compatBaseline);
102:                Map cNameToMethod = buildNameToMethodMap(currentVersion);
103:
104:                CoIterator iter = new CoIterator(null, bNameToMethod.keySet(),
105:                        cNameToMethod.keySet());
106:
107:                while (iter.hasNext()) {
108:                    iter.next();
109:
110:                    String baselineMethodName = (String) iter.getLeft();
111:                    String currentMethodName = (String) iter.getRight();
112:
113:                    if (baselineMethodName == null) {
114:                        // a new method name has been added in the new version
115:                        List currentMethods = (List) cNameToMethod
116:                                .get(currentMethodName);
117:                        reportMethodsAdded(currentVersion, currentMethods);
118:                    } else if (currentMethodName == null) {
119:                        // all methods with name x have been removed from the old version
120:                        List baselineMethods = (List) bNameToMethod
121:                                .get(baselineMethodName);
122:                        reportMethodsRemoved(compatBaseline, baselineMethods,
123:                                currentVersion);
124:                    } else {
125:                        // assert baselineMethodName equals currentMethodName
126:
127:                        List baselineMethods = (List) bNameToMethod
128:                                .get(baselineMethodName);
129:                        List currentMethods = (List) cNameToMethod
130:                                .get(currentMethodName);
131:
132:                        filterSoftMatchedMethods(compatBaseline,
133:                                baselineMethods, currentVersion, currentMethods);
134:
135:                        filterChangedMethods(baselineMethodName,
136:                                compatBaseline, baselineMethods,
137:                                currentVersion, currentMethods);
138:
139:                        // if any methods are left, they have no matching method in
140:                        // the other version, so report as removed or added respectively.
141:
142:                        if (!baselineMethods.isEmpty()) {
143:                            reportMethodsRemoved(compatBaseline,
144:                                    baselineMethods, currentVersion);
145:                        }
146:
147:                        if (!currentMethods.isEmpty()) {
148:                            reportMethodsAdded(currentVersion, currentMethods);
149:                        }
150:                    }
151:                }
152:
153:                return true;
154:            }
155:
156:            /**
157:             * Given a list of old and new methods for a particular method name,
158:             * find the (old, new) method pairs which have identical argument lists.
159:             * <p>
160:             * For these:
161:             * <ul>
162:             *  <li>report on changes in accessibility, return type, etc
163:             *  <li>remove from the list
164:             * </ul>
165:             *
166:             * On return from this method, the old and new method lists contain only
167:             * methods whose argument lists have changed between versions [or possibly,
168:             * methods which have been deleted while one or more new methods of the
169:             * same name have been added, depending on how you view it]. All other
170:             * situations have been dealt with.
171:             * <p>
172:             * Note that one or both method lists may be empty on return from
173:             * this method.
174:             */
175:            private void filterSoftMatchedMethods(JavaType compatBaseline,
176:                    List baselineMethods, JavaType currentVersion,
177:                    List currentMethods) {
178:                for (Iterator bIter = baselineMethods.iterator(); bIter
179:                        .hasNext();) {
180:                    Method bMethod = (Method) bIter.next();
181:
182:                    for (Iterator cIter = currentMethods.iterator(); cIter
183:                            .hasNext();) {
184:                        Method cMethod = (Method) cIter.next();
185:
186:                        if (isSoftMatch(bMethod, cMethod)) {
187:                            check(compatBaseline, bMethod, cMethod);
188:                            bIter.remove();
189:                            cIter.remove();
190:                            break;
191:                        }
192:                    }
193:                }
194:            }
195:
196:            /**
197:             * Two methods are a "soft" match if they have the same name and argument
198:             * list. No two methods on the same class are ever a "soft match" for
199:             * each other, because the compiler requires distinct parameter lists for
200:             * overloaded methods. This also implies that for a given method on an "old"
201:             * class version, there are either zero or one "soft matches" on the new
202:             * version.
203:             * <p>
204:             * However a "soft match" is not sufficient to ensure binary compatibility.
205:             * A change in the method return type will result in a link error when used
206:             * with code compiled against the previous version of the class.
207:             * <p>
208:             * There may also be other differences between methods that are regarded
209:             * as "soft matches": the exceptions thrown, the deprecation status of the
210:             * methods, their accessibility, etc.
211:             */
212:            private boolean isSoftMatch(Method oldMethod, Method newMethod) {
213:                String oldName = oldMethod.getName();
214:                String newName = newMethod.getName();
215:
216:                if (!oldName.equals(newName)) {
217:                    return false;
218:                }
219:
220:                StringBuffer buf = new StringBuffer();
221:                appendHumanReadableArgTypeList(oldMethod, buf);
222:                String oldArgs = buf.toString();
223:
224:                buf.setLength(0);
225:                appendHumanReadableArgTypeList(newMethod, buf);
226:                String newArgs = buf.toString();
227:
228:                return (oldArgs.equals(newArgs));
229:            }
230:
231:            /**
232:             * For each method in the baselineMethods list, find the "best match"
233:             * in the currentMethods list, report the changes between this method
234:             * pair, then remove both methods from the lists.
235:             * <p>
236:             * On return, at least one of the method lists will be empty.
237:             */
238:            private void filterChangedMethods(String methodName,
239:                    JavaType compatBaseline, List baselineMethods,
240:                    JavaType currentVersion, List currentMethods) {
241:                // ok, we now have to deal with the tricky cases, where it is not
242:                // immediately obvious which old methods correspond to which new
243:                // methods.
244:                //
245:                // Here we build a similarity table, i.e. for new method i and old
246:                // method j we have number that charaterizes how similar the method
247:                // signatures are (0 means equal, higher number means more different)
248:
249:                while (!baselineMethods.isEmpty() && !currentMethods.isEmpty()) {
250:                    int[][] similarityTable = buildSimilarityTable(
251:                            baselineMethods, currentMethods);
252:
253:                    int min = Integer.MAX_VALUE;
254:                    int iMin = baselineMethods.size();
255:                    int jMin = currentMethods.size();
256:                    for (int i = 0; i < baselineMethods.size(); i++) {
257:                        for (int j = 0; j < currentMethods.size(); j++) {
258:                            final int tableEntry = similarityTable[i][j];
259:                            if (tableEntry < min) {
260:                                min = tableEntry;
261:                                iMin = i;
262:                                jMin = j;
263:                            }
264:                        }
265:                    }
266:                    Method iMethod = (Method) baselineMethods.remove(iMin);
267:                    Method jMethod = (Method) currentMethods.remove(jMin);
268:                    check(compatBaseline, iMethod, jMethod);
269:                }
270:            }
271:
272:            private int[][] buildSimilarityTable(List baselineMethods,
273:                    List currentMethods) {
274:                int[][] similarityTable = new int[baselineMethods.size()][currentMethods
275:                        .size()];
276:                for (int i = 0; i < baselineMethods.size(); i++) {
277:                    for (int j = 0; j < currentMethods.size(); j++) {
278:                        final Method iMethod = (Method) baselineMethods.get(i);
279:                        final Method jMethod = (Method) currentMethods.get(j);
280:                        similarityTable[i][j] = distance(iMethod, jMethod);
281:                    }
282:                }
283:                return similarityTable;
284:            }
285:
286:            private int distance(Method m1, Method m2) {
287:                final JavaType[] m1Args = m1.getArgumentTypes();
288:                final JavaType[] m2Args = m2.getArgumentTypes();
289:
290:                if (m1Args.length != m2Args.length) {
291:                    return 1000 * Math.abs(m1Args.length - m2Args.length);
292:                }
293:
294:                int retVal = 0;
295:                for (int i = 0; i < m1Args.length; i++) {
296:                    if (!m1Args[i].toString().equals(m2Args[i].toString())) {
297:                        retVal += 1;
298:                    }
299:                }
300:                return retVal;
301:            }
302:
303:            /**
304:             * Searches the class hierarchy for a method that has a certain signature.
305:             * @param methodSignature the sig we're looking for
306:             * @param clazz class where search starts
307:             * @return class name of a superclass of clazz, might be null
308:             */
309:            private String findSuperClassWithSignature(String methodSignature,
310:                    JavaType clazz) {
311:                final JavaType[] super Classes = clazz.getSuperClasses();
312:                for (int i = 0; i < super Classes.length; i++) {
313:                    JavaType super Class = super Classes[i];
314:                    final Method[] super Methods = super Class.getMethods();
315:                    for (int j = 0; j < super Methods.length; j++) {
316:                        Method super Method = super Methods[j];
317:                        final String super MethodSignature = getMethodId(
318:                                super Class, super Method);
319:                        if (methodSignature.equals(super MethodSignature)) {
320:                            return super Class.getName();
321:                        }
322:                    }
323:
324:                }
325:                return null;
326:            }
327:
328:            /**
329:             * Searches the class hierarchy for a method that has a certtain signature.
330:             * @param methodSignature the sig we're looking for
331:             * @param clazz class where search starts
332:             * @return class name of a superinterface of clazz, might be null
333:             */
334:            private String findSuperInterfaceWithSignature(
335:                    String methodSignature, JavaType clazz) {
336:                final JavaType[] super Classes = clazz.getAllInterfaces();
337:                for (int i = 0; i < super Classes.length; i++) {
338:                    JavaType super Class = super Classes[i];
339:                    final Method[] super Methods = super Class.getMethods();
340:                    for (int j = 0; j < super Methods.length; j++) {
341:                        Method super Method = super Methods[j];
342:                        final String super MethodSignature = getMethodId(
343:                                super Class, super Method);
344:                        if (methodSignature.equals(super MethodSignature)) {
345:                            return super Class.getName();
346:                        }
347:                    }
348:
349:                }
350:                return null;
351:            }
352:
353:            /**
354:             * Given a list of methods, report each one as being removed.
355:             */
356:            private void reportMethodsRemoved(JavaType baselineClass,
357:                    List baselineMethods, JavaType currentClass) {
358:                for (Iterator i = baselineMethods.iterator(); i.hasNext();) {
359:                    Method method = (Method) i.next();
360:                    reportMethodRemoved(baselineClass, method, currentClass);
361:                }
362:            }
363:
364:            /**
365:             * Report that a method has been removed from a class.
366:             * @param oldClass the class where the method was available
367:             * @param oldMethod the method that has been removed
368:             * @param currentClass the superclass where the method is now available, might be null
369:             */
370:            private void reportMethodRemoved(JavaType oldClass,
371:                    Method oldMethod, JavaType currentClass) {
372:                if (!scopeSelector.isSelected(oldMethod)) {
373:                    return;
374:                }
375:
376:                String signature = getMethodId(oldClass, oldMethod);
377:
378:                String oldBaseClassForMethod = findSuperClassWithSignature(
379:                        signature, oldClass);
380:                String oldInterfaceForMethod = findSuperInterfaceWithSignature(
381:                        signature, oldClass);
382:
383:                String newBaseClassForMethod = findSuperClassWithSignature(
384:                        signature, currentClass);
385:                String newInterfaceForMethod = findSuperInterfaceWithSignature(
386:                        signature, currentClass);
387:
388:                boolean oldInheritedMethod = (oldBaseClassForMethod != null)
389:                        || (oldInterfaceForMethod != null);
390:                boolean newInheritedMethod = (newBaseClassForMethod != null)
391:                        || (newInterfaceForMethod != null);
392:
393:                if (oldInheritedMethod && newInheritedMethod) {
394:                    // Previously, this method overrode an inherited definition.
395:                    // The current version of the class doesn't have this
396:                    // method, but a parent class or interface still does, so this
397:                    // does not cause an incompatibility.
398:                    fireDiff(MSG_METHOD_OVERRIDE_REMOVED, Severity.INFO,
399:                            oldClass, oldMethod, null);
400:                } else if (oldInheritedMethod) {
401:                    // Previously, this method override an inherited definition.
402:                    // It isn't present in the current class, though, and neither is
403:                    // it present in the new class' ancestors. Best to just
404:                    // report it as removed...
405:                    fireDiff(MSG_METHOD_REMOVED, getSeverity(oldClass,
406:                            oldMethod, Severity.ERROR), oldClass, oldMethod,
407:                            null);
408:                } else if (newBaseClassForMethod != null) {
409:                    // Previously, this method didn't override anything. The current
410:                    // version of this class doesn't have this method any more,
411:                    // but an ancestor class now *does*. This is an instance
412:                    // of the pull-up refactoring pattern, where a method is moved
413:                    // to an ancestor class.
414:                    fireDiff(MSG_METHOD_NOW_IN_SUPERCLASS, Severity.INFO,
415:                            oldClass, oldMethod,
416:                            new String[] { newBaseClassForMethod });
417:                } else if (newInterfaceForMethod != null) {
418:                    // Previously, this method didn't override anything. The current
419:                    // version of this class doesn't have this method any more,
420:                    // but one of the implemented interfaces now *does*. This is an
421:                    // instance of the pull-up refactoring pattern, where a method is
422:                    // moved to an interface.
423:                    fireDiff(MSG_METHOD_NOW_IN_INTERFACE, Severity.INFO,
424:                            oldClass, oldMethod,
425:                            new String[] { newInterfaceForMethod });
426:                } else {
427:                    // This method wasn't anything special in the old class, and
428:                    // it isn't present in the new class either directly or via
429:                    // inheritance.
430:                    fireDiff(MSG_METHOD_REMOVED, getSeverity(oldClass,
431:                            oldMethod, Severity.ERROR), oldClass, oldMethod,
432:                            null);
433:                }
434:            }
435:
436:            /**
437:             * Given a list of methods, report each one as being added.
438:             */
439:            private void reportMethodsAdded(JavaType currentClass,
440:                    List currentMethods) {
441:                for (Iterator i = currentMethods.iterator(); i.hasNext();) {
442:                    Method method = (Method) i.next();
443:                    reportMethodAdded(currentClass, method);
444:                }
445:            }
446:
447:            /**
448:             * Report that a method has been added to a class.
449:             */
450:            private void reportMethodAdded(JavaType newClass, Method newMethod) {
451:                if (!scopeSelector.isSelected(newMethod)) {
452:                    return;
453:                }
454:
455:                if (newClass.isInterface()) {
456:                    // TODO: this is not an incompatibility if the new method
457:                    // actually already exists on a parent interface of the
458:                    // old interface. In that case, any class implementing the
459:                    // old version of this interface must already have an
460:                    // implementation of this method. See bugtracker #961217
461:                    fireDiff(MSG_METHOD_ADDED_TO_INTERFACE, getSeverity(
462:                            newClass, newMethod, Severity.ERROR), newClass,
463:                            newMethod, null);
464:                } else if (newMethod.isAbstract()) {
465:                    // TODO: this is not an incompatibility if the new method
466:                    // actually already exists on a parent interface of the
467:                    // old interface and was abstract. In that case, any class
468:                    // extending the old version of this class must already
469:                    // have an implementation of this method.
470:                    //
471:                    // Note that abstract methods can never be package or private
472:                    // scope, so we don't need to use the getSeverity method.
473:                    fireDiff(MSG_ABSTRACT_METHOD_ADDED, Severity.ERROR,
474:                            newClass, newMethod, null);
475:                } else {
476:                    // TODO:
477:                    //. (a) check whether this method exists on a parent of the
478:                    //  new class. If so, indicate that this new method is overriding
479:                    //  some inherited method.
480:                    //  (b) if not a, then check whether this method exists on a parent
481:                    //  of the old class. If so, then report that the method has
482:                    //  been moved from the parent to the child class. This is
483:                    //  potentially useful info for the user.
484:                    //
485:                    // See bugtracker #959225
486:                    fireDiff(MSG_METHOD_ADDED, Severity.INFO, newClass,
487:                            newMethod, null);
488:                }
489:            }
490:
491:            /**
492:             * Builds a map from a method name to a List of methods.
493:             */
494:            private Map buildNameToMethodMap(JavaType clazz) {
495:                Method[] methods = clazz.getMethods();
496:                Map retVal = new HashMap();
497:                for (int i = 0; i < methods.length; i++) {
498:                    Method method = methods[i];
499:
500:                    final String name = method.getName();
501:                    List set = (List) retVal.get(name);
502:                    if (set == null) {
503:                        set = new ArrayList();
504:                        retVal.put(name, set);
505:                    }
506:                    set.add(method);
507:                }
508:                return retVal;
509:            }
510:
511:            private void check(JavaType compatBaseline, Method baselineMethod,
512:                    Method currentMethod) {
513:                if (!scopeSelector.isSelected(baselineMethod)
514:                        && !scopeSelector.isSelected(currentMethod)) {
515:                    return;
516:                }
517:
518:                checkParameterTypes(compatBaseline, baselineMethod,
519:                        currentMethod);
520:                checkReturnType(compatBaseline, baselineMethod, currentMethod);
521:                checkDeclaredExceptions(compatBaseline, baselineMethod,
522:                        currentMethod);
523:                checkDeprecated(compatBaseline, baselineMethod, currentMethod);
524:                checkVisibility(compatBaseline, baselineMethod, currentMethod);
525:                checkFinal(compatBaseline, baselineMethod, currentMethod);
526:            }
527:
528:            private void checkParameterTypes(JavaType compatBaseline,
529:                    Method baselineMethod, Method currentMethod) {
530:                JavaType[] bArgs = baselineMethod.getArgumentTypes();
531:                JavaType[] cArgs = currentMethod.getArgumentTypes();
532:
533:                if (bArgs.length != cArgs.length) {
534:                    fireDiff(MSG_METHOD_ARGCOUNT_CHANGED, getSeverity(
535:                            compatBaseline, baselineMethod, Severity.ERROR),
536:                            compatBaseline, baselineMethod, null);
537:                    return;
538:                }
539:
540:                //System.out.println("baselineMethod = " + getMethodId(compatBaseline, baselineMethod));
541:                for (int i = 0; i < bArgs.length; i++) {
542:                    JavaType bArg = bArgs[i];
543:                    JavaType cArg = cArgs[i];
544:
545:                    if (bArg.getName().equals(cArg.getName())) {
546:                        continue;
547:                    }
548:
549:                    // TODO: Check assignability...
550:                    String[] args = { "" + (i + 1), cArg.toString() };
551:                    fireDiff(MSG_METHOD_PARAMTYPE_CHANGED, getSeverity(
552:                            compatBaseline, baselineMethod, Severity.ERROR),
553:                            compatBaseline, baselineMethod, args);
554:                }
555:            }
556:
557:            private void checkReturnType(JavaType compatBaseline,
558:                    Method baselineMethod, Method currentMethod) {
559:                JavaType bReturnType = baselineMethod.getReturnType();
560:                JavaType cReturnType = currentMethod.getReturnType();
561:
562:                // TODO: Check assignability. If the new return type is
563:                // assignable to the old type, then the code is source-code
564:                // compatible even when binary-incompatible.
565:                if (!bReturnType.toString().equals(cReturnType.toString())) {
566:                    fireDiff(MSG_METHOD_RETURNTYPE_CHANGED, getSeverity(
567:                            compatBaseline, baselineMethod, Severity.ERROR),
568:                            compatBaseline, baselineMethod,
569:                            new String[] { cReturnType.toString() });
570:                }
571:            }
572:
573:            private void checkDeclaredExceptions(JavaType compatBaseline,
574:                    Method baselineMethod, Method currentMethod) {
575:                // TODO
576:            }
577:
578:            private void checkDeprecated(JavaType compatBaseline,
579:                    Method baselineMethod, Method currentMethod) {
580:                boolean bIsDeprecated = baselineMethod.isDeprecated();
581:                boolean cIsDeprecated = currentMethod.isDeprecated();
582:
583:                if (bIsDeprecated && !cIsDeprecated) {
584:                    fireDiff(MSG_METHOD_UNDEPRECATED, Severity.INFO,
585:                            compatBaseline, baselineMethod, null);
586:                } else if (!bIsDeprecated && cIsDeprecated) {
587:                    fireDiff(MSG_METHOD_DEPRECATED, Severity.INFO,
588:                            compatBaseline, baselineMethod, null);
589:                }
590:            }
591:
592:            /**
593:             * Report changes in the declared accessibility of a method
594:             * (public/protected/etc).
595:             */
596:            private void checkVisibility(JavaType compatBaseline,
597:                    Method baselineMethod, Method currentMethod) {
598:                Scope bScope = baselineMethod.getEffectiveScope();
599:                Scope cScope = currentMethod.getEffectiveScope();
600:
601:                if (cScope.isLessVisibleThan(bScope)) {
602:                    String[] args = { bScope.getDesc(), cScope.getDesc() };
603:                    fireDiff(MSG_METHOD_LESS_ACCESSIBLE, getSeverity(
604:                            compatBaseline, baselineMethod, Severity.ERROR),
605:                            compatBaseline, baselineMethod, args);
606:                } else if (cScope.isMoreVisibleThan(bScope)) {
607:                    String[] args = { bScope.getDesc(), cScope.getDesc() };
608:                    fireDiff(MSG_METHOD_MORE_ACCESSIBLE, Severity.INFO,
609:                            compatBaseline, baselineMethod, args);
610:                }
611:            }
612:
613:            private void checkFinal(JavaType compatBaseline,
614:                    Method baselineMethod, Method currentMethod) {
615:                boolean bIsFinal = baselineMethod.isFinal();
616:                boolean cIsFinal = currentMethod.isFinal();
617:
618:                if (bIsFinal && !cIsFinal) {
619:                    fireDiff(MSG_METHOD_NOW_NONFINAL, Severity.INFO,
620:                            compatBaseline, baselineMethod, null);
621:                } else if (!bIsFinal && cIsFinal) {
622:                    fireDiff(MSG_METHOD_NOW_FINAL, Severity.ERROR,
623:                            compatBaseline, baselineMethod, null);
624:                }
625:            }
626:
627:            /**
628:             * Creates a human readable String that is similar to the method signature
629:             * and identifies the method within a class.
630:             * @param clazz the container of the method
631:             * @param method the method to identify.
632:             * @return a human readable id, for example "public void print(java.lang.String)"
633:             */
634:            private String getMethodId(JavaType clazz, Method method) {
635:                StringBuffer buf = new StringBuffer();
636:
637:                final String scopeDecl = method.getDeclaredScope().getDecl();
638:                if (scopeDecl.length() > 0) {
639:                    buf.append(scopeDecl);
640:                    buf.append(" ");
641:                }
642:
643:                String name = method.getName();
644:                if ("<init>".equals(name)) {
645:                    final String className = clazz.getName();
646:                    int idx = className.lastIndexOf('.');
647:                    name = className.substring(idx + 1);
648:                } else {
649:                    buf.append(method.getReturnType());
650:                    buf.append(' ');
651:                }
652:                buf.append(name);
653:                buf.append('(');
654:                appendHumanReadableArgTypeList(method, buf);
655:                buf.append(')');
656:                return buf.toString();
657:            }
658:
659:            private void appendHumanReadableArgTypeList(Method method,
660:                    StringBuffer buf) {
661:                JavaType[] argTypes = method.getArgumentTypes();
662:                String argSeparator = "";
663:                for (int i = 0; i < argTypes.length; i++) {
664:                    buf.append(argSeparator);
665:                    buf.append(argTypes[i].getName());
666:                    argSeparator = ", ";
667:                }
668:            }
669:
670:            private void fireDiff(Message msg, Severity severity,
671:                    JavaType clazz, Method method, String[] args) {
672:                final String className = clazz.getName();
673:                final ApiDifference diff = new ApiDifference(msg, severity,
674:                        className, getMethodId(clazz, method), null, args);
675:                getApiDiffDispatcher().fireDiff(diff);
676:            }
677:
678:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.