001: /*
002: * FindBugs - Find Bugs in Java programs
003: * Copyright (C) 2005, University of Maryland
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 edu.umd.cs.findbugs.model;
021:
022: import java.util.HashMap;
023: import java.util.HashSet;
024: import java.util.Iterator;
025: import java.util.Locale;
026: import java.util.Map;
027: import java.util.Set;
028:
029: import edu.umd.cs.findbugs.BugAnnotation;
030: import edu.umd.cs.findbugs.BugCollection;
031: import edu.umd.cs.findbugs.BugInstance;
032: import edu.umd.cs.findbugs.ClassAnnotation;
033: import edu.umd.cs.findbugs.SystemProperties;
034:
035: /**
036: * Build a map of added class names to removed class names.
037: * Serves as a ClassNameRewriter that can match up renamed classes
038: * in two BugCollections.
039: *
040: * @author David Hovemeyer
041: */
042: public class MovedClassMap implements ClassNameRewriter {
043:
044: private static final boolean DEBUG = SystemProperties
045: .getBoolean("movedClasses.debug");
046:
047: private BugCollection before;
048: private BugCollection after;
049: private Map<String, String> rewriteMap;
050:
051: public MovedClassMap(BugCollection before, BugCollection after) {
052: this .before = before;
053: this .after = after;
054: this .rewriteMap = new HashMap<String, String>();
055: }
056:
057: public MovedClassMap execute() {
058: Set<String> beforeClasses = buildClassSet(before);
059: Set<String> afterClasses = buildClassSet(after);
060:
061: Set<String> removedClasses = new HashSet<String>(beforeClasses);
062: removedClasses.removeAll(afterClasses);
063:
064: Set<String> addedClasses = new HashSet<String>(afterClasses);
065: addedClasses.removeAll(beforeClasses);
066:
067: Map<String, String> removedShortNameToFullNameMap = buildShortNameToFullNameMap(removedClasses);
068:
069: // Map names of added classes to names of removed classes if
070: // they have the same short name.
071: for (String fullAddedName : addedClasses) {
072:
073: // FIXME: could use a similarity metric to match added and removed
074: // classes. Instead, we just match based on the short class name.
075:
076: String shortAddedName = getShortClassName(fullAddedName);
077: String fullRemovedName = removedShortNameToFullNameMap
078: .get(shortAddedName);
079: if (fullRemovedName != null) {
080: if (DEBUG)
081: System.err.println(fullAddedName + " --> "
082: + fullRemovedName);
083: rewriteMap.put(fullAddedName, fullRemovedName);
084: }
085:
086: }
087:
088: return this ;
089: }
090:
091: public boolean isEmpty() {
092: return rewriteMap.isEmpty();
093: }
094:
095: public String rewriteClassName(String className) {
096: String rewrittenClassName = rewriteMap.get(className);
097: if (rewrittenClassName != null) {
098: className = rewrittenClassName;
099: }
100: return className;
101: }
102:
103: /**
104: * Find set of classes referenced in given BugCollection.
105: *
106: * @param bugCollection
107: * @return set of classes referenced in the BugCollection
108: */
109: private Set<String> buildClassSet(BugCollection bugCollection) {
110: Set<String> classSet = new HashSet<String>();
111:
112: for (Iterator<BugInstance> i = bugCollection.iterator(); i
113: .hasNext();) {
114: BugInstance warning = i.next();
115: for (Iterator<BugAnnotation> j = warning
116: .annotationIterator(); j.hasNext();) {
117: BugAnnotation annotation = j.next();
118: if (!(annotation instanceof ClassAnnotation))
119: continue;
120: classSet.add(((ClassAnnotation) annotation)
121: .getClassName());
122: }
123: }
124:
125: return classSet;
126: }
127:
128: /**
129: * Build a map of short class names (without package) to full
130: * class names.
131: *
132: * @param classSet set of fully-qualified class names
133: * @return map of short class names to fully-qualified class names
134: */
135: private Map<String, String> buildShortNameToFullNameMap(
136: Set<String> classSet) {
137: Map<String, String> result = new HashMap<String, String>();
138: for (String className : classSet) {
139: String shortClassName = getShortClassName(className);
140: result.put(shortClassName, className);
141: }
142: return result;
143: }
144:
145: /**
146: * Get a short class name (no package part).
147: *
148: * @param className a class name
149: * @return short class name
150: */
151: private String getShortClassName(String className) {
152: int lastDot = className.lastIndexOf('.');
153: if (lastDot >= 0) {
154: className = className.substring(lastDot + 1);
155: }
156: return className.toLowerCase(Locale.US).replace('+', '$');
157: }
158:
159: }
|