/* * FindBugs - Find Bugs in Java programs * Copyright (C) 2005, University of Maryland * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package edu.umd.cs.findbugs.model; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.Locale; import java.util.Map; import java.util.Set; import edu.umd.cs.findbugs.BugAnnotation; import edu.umd.cs.findbugs.BugCollection; import edu.umd.cs.findbugs.BugInstance; import edu.umd.cs.findbugs.ClassAnnotation; import edu.umd.cs.findbugs.SystemProperties; /** * Build a map of added class names to removed class names. Serves as a * ClassNameRewriter that can match up renamed classes in two BugCollections. * * @author David Hovemeyer */ public class MovedClassMap implements ClassNameRewriter { private static final boolean DEBUG = SystemProperties.getBoolean("movedClasses.debug"); private BugCollection before; private BugCollection after; private Map<String, String> rewriteMap; public MovedClassMap(BugCollection before, BugCollection after) { this.before = before; this.after = after; this.rewriteMap = new HashMap<String, String>(); } public MovedClassMap execute() { Set<String> beforeClasses = buildClassSet(before); Set<String> afterClasses = buildClassSet(after); Set<String> removedClasses = new HashSet<String>(beforeClasses); removedClasses.removeAll(afterClasses); Set<String> addedClasses = new HashSet<String>(afterClasses); addedClasses.removeAll(beforeClasses); Map<String, String> removedShortNameToFullNameMap = buildShortNameToFullNameMap(removedClasses); // Map names of added classes to names of removed classes if // they have the same short name. for (String fullAddedName : addedClasses) { // FIXME: could use a similarity metric to match added and removed // classes. Instead, we just match based on the short class name. String shortAddedName = getShortClassName(fullAddedName); String fullRemovedName = removedShortNameToFullNameMap.get(shortAddedName); if (fullRemovedName != null) { if (DEBUG) System.err.println(fullAddedName + " --> " + fullRemovedName); rewriteMap.put(fullAddedName, fullRemovedName); } } return this; } public boolean isEmpty() { return rewriteMap.isEmpty(); } public String rewriteClassName(String className) { String rewrittenClassName = rewriteMap.get(className); if (rewrittenClassName != null) { className = rewrittenClassName; } return className; } /** * Find set of classes referenced in given BugCollection. * * @param bugCollection * @return set of classes referenced in the BugCollection */ private Set<String> buildClassSet(BugCollection bugCollection) { Set<String> classSet = new HashSet<String>(); for (Iterator<BugInstance> i = bugCollection.iterator(); i.hasNext();) { BugInstance warning = i.next(); for (Iterator<BugAnnotation> j = warning.annotationIterator(); j.hasNext();) { BugAnnotation annotation = j.next(); if (!(annotation instanceof ClassAnnotation)) continue; classSet.add(((ClassAnnotation) annotation).getClassName()); } } return classSet; } /** * Build a map of short class names (without package) to full class names. * * @param classSet * set of fully-qualified class names * @return map of short class names to fully-qualified class names */ private Map<String, String> buildShortNameToFullNameMap(Set<String> classSet) { Map<String, String> result = new HashMap<String, String>(); for (String className : classSet) { String shortClassName = getShortClassName(className); result.put(shortClassName, className); } return result; } /** * Get a short class name (no package part). * * @param className * a class name * @return short class name */ private String getShortClassName(String className) { int lastDot = className.lastIndexOf('.'); if (lastDot >= 0) { className = className.substring(lastDot + 1); } return className.toLowerCase(Locale.US).replace('+', '$'); } }